├── .devcontainer ├── Dockerfile ├── devcontainer.json └── setup.sh ├── .editorconfig ├── .github ├── dependabot.yml ├── release.yml └── workflows │ ├── Benchmarks.yml │ ├── CargoAudit.yml │ ├── CleanUp.yml │ ├── CreateDevcontainerImage.yml │ ├── CreateRelease.yml │ ├── CreateReleaseBranch.yml │ ├── IssueLabelChecker.yml │ ├── PRLabelChecker.yml │ ├── README.md │ ├── ValidatePullRequest.yml │ ├── auto-merge-dependabot.yml │ ├── dep_build_wasm_examples.yml │ ├── dep_cargo_publish.yml │ └── dep_rust.yml ├── .gitignore ├── .vscode ├── launch.json └── settings.json ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Cargo.lock ├── Cargo.toml ├── Justfile ├── LICENSE.txt ├── README.md ├── RustDev.md ├── SECURITY.md ├── SUPPORT.md ├── docs ├── github-labels.md └── observability.md ├── rust-toolchain.toml ├── rustfmt.toml ├── src ├── examples_common │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── hyperlight_wasm │ ├── Cargo.toml │ ├── benches │ │ └── benchmarks.rs │ ├── build.rs │ ├── examples │ │ ├── helloworld │ │ │ └── main.rs │ │ ├── hostfuncs │ │ │ └── main.rs │ │ ├── metrics │ │ │ └── main.rs │ │ └── rust_wasm_examples │ │ │ └── main.rs │ ├── scripts │ │ ├── build-wasm-examples.bat │ │ └── build-wasm-examples.sh │ └── src │ │ ├── build_info.rs │ │ ├── lib.rs │ │ └── sandbox │ │ ├── loaded_wasm_sandbox.rs │ │ ├── metrics.rs │ │ ├── mod.rs │ │ ├── proto_wasm_sandbox.rs │ │ ├── sandbox_builder.rs │ │ └── wasm_sandbox.rs ├── hyperlight_wasm_aot │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── hyperlight_wasm_macro │ ├── Cargo.toml │ └── src │ │ ├── lib.rs │ │ └── wasmguest.rs ├── rust_wasm_samples │ ├── .cargo │ │ └── config.toml │ ├── Cargo.lock │ ├── Cargo.toml │ ├── build.rs │ └── src │ │ └── lib.rs ├── scripts │ └── auto-approve-dependabot.sh ├── wasm_runtime │ ├── .cargo │ │ └── config.toml │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── build.rs │ └── src │ │ ├── component.rs │ │ ├── hostfuncs.rs │ │ ├── main.rs │ │ ├── marshal.rs │ │ ├── module.rs │ │ ├── platform.c │ │ ├── platform.rs │ │ └── wasip1.rs └── wasmsamples │ ├── .gitignore │ ├── HelloWorld.c │ ├── HostFunction.c │ ├── RunWasm.c │ ├── compile-wasm.bat │ └── dockerfile └── typos.toml /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | ## Dockerfile for devcontainer 2 | 3 | FROM ghcr.io/hyperlight-dev/hyperlight-devcontainer:latest 4 | ARG USER=vscode 5 | ARG GROUP=vscode 6 | 7 | # use root to install tools then switch back to vscode user 8 | USER root 9 | 10 | # Install dependencies 11 | RUN apt-get update \ 12 | && apt-get -y install \ 13 | netcat-openbsd 14 | 15 | ARG GCC_VERSION=12 16 | 17 | RUN apt-get install -y g++-multilib \ 18 | && apt-get install -y libgcc-${GCC_VERSION}-dev \ 19 | && apt-get install -y lib32gcc-${GCC_VERSION}-dev 20 | 21 | ARG WASI_SDK_VERSION_FULL=20.0 22 | ARG WASI_SDK_VERSION_MAJOR=${WASI_SDK_VERSION_FULL%%.*} 23 | 24 | # Install wasi-sdk 25 | RUN wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-${WASI_SDK_VERSION_MAJOR}/wasi-sdk-${WASI_SDK_VERSION_FULL}-linux.tar.gz \ 26 | && tar xvf wasi-sdk-${WASI_SDK_VERSION_FULL}-linux.tar.gz \ 27 | && rm wasi-sdk-${WASI_SDK_VERSION_FULL}-linux.tar.gz \ 28 | && mv /wasi-sdk-${WASI_SDK_VERSION_FULL} /opt/wasi-sdk 29 | 30 | USER $USER 31 | ARG RUST_TOOLCHAIN=1.82.0 32 | 33 | # Install rust and component tools 34 | RUN rustup default ${RUST_TOOLCHAIN} \ 35 | && cargo install --locked wasm-tools \ 36 | && cargo install wkg \ 37 | && cargo install wac-cli \ 38 | && cargo install cargo-component --locked 39 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For more info on the configuration below, check out the link: 2 | // https://code.visualstudio.com/docs/devcontainers/create-dev-container 3 | { 4 | "name": "Hyperlight-Wasm", 5 | "image": "ghcr.io/hyperlight-dev/hyperlight-wasm-devcontainer:latest", 6 | 7 | "containerUser": "vscode", 8 | // Environment for the container also used by the `postCreateCommand` 9 | "containerEnv": { 10 | "DEVICE": "/dev/kvm", 11 | "REMOTE_USER": "vscode", 12 | "REMOTE_GROUP": "vscode" 13 | }, 14 | 15 | "runArgs": [ 16 | "--device=/dev/kvm" 17 | ], 18 | 19 | // use `postStartCommand` for additional setup commands 20 | // this is run after the container is created and the user has been added 21 | "postStartCommand": "bash .devcontainer/setup.sh", 22 | 23 | "customizations": { 24 | "vscode": { 25 | "extensions": [ 26 | "ms-vscode.cpptools-extension-pack", 27 | "ms-vscode.cmake-tools", 28 | "rust-lang.rust-analyzer", 29 | "vadimcn.vscode-lldb" 30 | ], 31 | "settings": { 32 | "rust-analyzer.rustfmt.extraArgs": [ 33 | "+nightly" // required for rustfmt.toml which uses nightly features 34 | ], 35 | // This is needed to allow tests to find files when running under the debugger 36 | "rust-analyzer.runnables.extraEnv": { 37 | "RUST_DIR_FOR_DEBUGGING_TESTS": "${workspaceFolder}/src/hyperlight_wasm" 38 | } 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /.devcontainer/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Change device ownership 4 | sudo chown -R $REMOTE_USER:$REMOTE_GROUP $DEVICE 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Reference: https://github.com/dotnet/roslyn/blob/main/.editorconfig 2 | # EditorConfig is awesome: https://EditorConfig.org 3 | 4 | # top-most EditorConfig file 5 | root = true 6 | [*] 7 | indent_style = space 8 | indent_size = 4 9 | insert_final_newline = true 10 | charset = utf-8 11 | 12 | # JSON files 13 | [*.json] 14 | indent_size = 2 15 | 16 | # YAML files 17 | [*.yml] 18 | indent_size = 2 19 | [*.yaml] 20 | indent_size = 2 21 | 22 | # Powershell files 23 | [*.ps1] 24 | indent_size = 2 25 | 26 | # Shell script files 27 | [*.sh] 28 | end_of_line = lf 29 | indent_size = 2 30 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | time: "03:00" 8 | target-branch: "main" 9 | labels: 10 | - "kind/dependencies" 11 | - package-ecosystem: "cargo" 12 | directory: "/" 13 | schedule: 14 | interval: "daily" 15 | time: "03:00" 16 | target-branch: "main" 17 | labels: 18 | - "kind/dependencies" 19 | -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | # .github/release.yml 2 | 3 | changelog: 4 | categories: 5 | - title: Full Changelog (excl. dependencies) 6 | labels: 7 | - "*" 8 | - title: Full Changelog (dependencies) 9 | labels: 10 | - kind/dependencies 11 | -------------------------------------------------------------------------------- /.github/workflows/Benchmarks.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json 2 | 3 | name: Benchmarks 4 | 5 | on: 6 | workflow_call: # this is called from CreateRelease.yml 7 | 8 | # The reason for default shell bash is because on our self-hosted windows runners, 9 | # the default shell is powershell, which doesn't work correctly together with `just` commands. 10 | # Even if a command inside a just-recipe fails, github reports the step as successful. 11 | # The problem may or may not be related to our custom windows runner not applying the 12 | # powershell steps outlined here 13 | # https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#exit-codes-and-error-action-preference 14 | defaults: 15 | run: 16 | shell: bash 17 | 18 | jobs: 19 | build-wasm-examples: 20 | uses: ./.github/workflows/dep_build_wasm_examples.yml 21 | 22 | benchmark: 23 | needs: 24 | - build-wasm-examples 25 | strategy: 26 | fail-fast: true 27 | matrix: 28 | hypervisor: [hyperv, mshv, mshv3, kvm] # hyperv is windows, mshv and kvm are linux 29 | cpu: [amd, intel] 30 | config: [release] # don't want to benchmark debug-builds 31 | 32 | runs-on: ${{ fromJson(format('["self-hosted", "{0}", "X64", "1ES.Pool=hld-{1}-{2}"]', matrix.hypervisor == 'hyperv' && 'Windows' || 'Linux', matrix.hypervisor == 'hyperv' && 'win2022' || matrix.hypervisor == 'mshv3' && 'azlinux3-mshv' || matrix.hypervisor, matrix.cpu)) }} 33 | 34 | steps: 35 | - uses: actions/checkout@v4 36 | 37 | - uses: hyperlight-dev/ci-setup-workflow@v1.4.0 38 | with: 39 | rust-toolchain: "1.82.0" 40 | 41 | - name: Build Wasm Runtime Binary 42 | working-directory: ./src/hyperlight_wasm 43 | run: just build-wasm-runtime ${{ matrix.config }} 44 | 45 | - uses: dtolnay/rust-toolchain@1.82.0 46 | with: 47 | components: clippy, rustfmt 48 | 49 | - name: Download Wasm Modules 50 | uses: actions/download-artifact@v4 51 | with: 52 | name: guest-modules 53 | path: ./x64/${{ matrix.config }} 54 | 55 | ### Benchmarks ### 56 | 57 | # Install GH cli (needed for just bench-download) 58 | - name: Install github-cli (Linux mariner) 59 | if: runner.os == 'Linux' && matrix.hypervisor == 'mshv' || matrix.hypervisor == 'mshv3' 60 | run: sudo dnf install gh -y 61 | 62 | - name: Install github-cli (Linux ubuntu) 63 | if: runner.os == 'Linux' && matrix.hypervisor == 'kvm' 64 | run: sudo apt install gh -y 65 | 66 | - name: Fetch tags 67 | run: | 68 | git fetch --tags origin 69 | 70 | - name: Download benchmark from most recent release 71 | run: | 72 | just bench-download ${{ runner.os }} ${{ matrix.hypervisor }} 73 | continue-on-error: true 74 | working-directory: ./src/hyperlight_wasm 75 | env: 76 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 77 | 78 | - name: Run Benchmarks 79 | run: | 80 | just bench-ci dev release 81 | working-directory: ./src/hyperlight_wasm 82 | 83 | - name: Upload Benchmarks 84 | uses: actions/upload-artifact@v4 85 | with: 86 | name: benchmarks_${{runner.os}}_${{matrix.hypervisor}} 87 | path: ./target/criterion/ 88 | if-no-files-found: error -------------------------------------------------------------------------------- /.github/workflows/CargoAudit.yml: -------------------------------------------------------------------------------- 1 | name: Audit cargo dependencies for security vulnerabilities 2 | on: 3 | schedule: 4 | - cron: "0 8 * * 1" # run at 8am every Monday 5 | workflow_dispatch: # allow manual triggering 6 | 7 | permissions: 8 | issues: write # Creates issues for any vulnerabilities found 9 | contents: read 10 | checks: write # Needs to create check 11 | 12 | jobs: 13 | audit: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v4 17 | 18 | # We are not using the common workflow here because it installs a lot of tools we don't need 19 | - uses: dtolnay/rust-toolchain@master 20 | with: 21 | toolchain: "1.82.0" 22 | 23 | - uses: extractions/setup-just@v3 24 | with: 25 | just-version: "1.27" 26 | 27 | - uses: rustsec/audit-check@v2.0.0 28 | with: 29 | token: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/CleanUp.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json 2 | # This job cleans up old pre-releases and pre-releases packages retaining the last 40 versions 3 | 4 | name: Clean up old pre-releases and packages 5 | on: 6 | schedule: 7 | - cron: '0 8 * * 1' # run at 8am every Monday 8 | workflow_dispatch: # allow manual triggering 9 | permissions: 10 | actions: write # required for reading & deleting github actions artifacts 11 | contents: write # required for reading releases 12 | jobs: 13 | cleanup: 14 | name: Clean up old pre-releases and packages 15 | runs-on: ubuntu-latest 16 | steps: 17 | # https://github.com/marketplace/actions/delete-releases 18 | - name: Delete old 'hyperlight-wasm' releases 19 | uses: sgpublic/delete-release-action@v1.2 20 | with: 21 | release-drop: false 22 | pre-release-drop: true 23 | pre-release-keep-count: 5 24 | pre-release-drop-tag: true 25 | draft-drop: false 26 | env: 27 | GITHUB_TOKEN: ${{ github.token }} 28 | -------------------------------------------------------------------------------- /.github/workflows/CreateDevcontainerImage.yml: -------------------------------------------------------------------------------- 1 | name: Create and publish devcontainer Docker image 2 | 3 | on: 4 | push: 5 | branches: 6 | - "main" 7 | paths: 8 | - ".devcontainer/Dockerfile" 9 | - ".github/workflows/CreateDevcontainerImage.yml" 10 | - "rust-toolchain.toml" 11 | 12 | # Defines two custom environment variables for the workflow. These are used for the Container registry domain, and a name for the Docker image that this workflow builds. 13 | env: 14 | REGISTRY: ghcr.io 15 | IMAGE_NAME: ${{ github.repository }}-devcontainer 16 | USER: vscode 17 | GROUP: vscode 18 | LLVM_VERSION: 17 19 | RUST_TOOLCHAIN_DEFAULT: 1.82.0 20 | RUST_TOOLCHAIN_FILE: rust-toolchain.toml 21 | WASI_SDK_VERSION_FULL: "20.0" 22 | GCC_VERSION: "12" 23 | 24 | # There is a single job in this workflow. It's configured to run on the latest available version of Ubuntu. 25 | jobs: 26 | build-and-push-image: 27 | runs-on: ubuntu-latest 28 | # Sets the permissions granted to the `GITHUB_TOKEN` for the actions in this job. 29 | permissions: 30 | contents: read 31 | packages: write 32 | 33 | steps: 34 | - name: Checkout repository 35 | uses: actions/checkout@v4 36 | 37 | - name: Read Rust toolchain version from ${{ env.RUST_TOOLCHAIN_FILE }} 38 | id: toolchain 39 | run: | 40 | version=$(cat ${{ env.RUST_TOOLCHAIN_FILE }} | sed -n '/\[toolchain\]/,/^\[/{/^\s*channel = /s/[^"]*"\([^"]*\)".*/\1/p}') 41 | cat ${{ env.RUST_TOOLCHAIN_FILE }} | grep $version &> /dev/null \ 42 | && echo "RUST_TOOLCHAIN=${version}" >> "$GITHUB_OUTPUT" \ 43 | || echo "RUST_TOOLCHAIN=${{ env.RUST_TOOLCHAIN_FILE }}" >> "$GITHUB_OUTPUT" 44 | 45 | # Uses the `docker/login-action` action to log in to the Container registry registry using the account and password that will publish the packages. Once published, the packages are scoped to the account defined here. 46 | - name: Log in to the Container registry 47 | uses: docker/login-action@v3 48 | with: 49 | registry: ${{ env.REGISTRY }} 50 | username: ${{ github.actor }} 51 | password: ${{ secrets.GITHUB_TOKEN }} 52 | 53 | - name: Extract metadata (tags, labels) for Docker 54 | id: meta 55 | uses: docker/metadata-action@v5 56 | with: 57 | images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} 58 | 59 | - name: Build and push Docker image 60 | id: push 61 | uses: docker/build-push-action@v6 62 | with: 63 | context: ./.devcontainer 64 | push: true 65 | tags: | 66 | ${{ steps.meta.outputs.tags }} 67 | ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest 68 | labels: ${{ steps.meta.outputs.labels }} 69 | build-args: | 70 | USER=${{ env.USER }} 71 | GROUP=${{ env.GROUP }} 72 | LLVM_VERSION=${{ env.LLVM_VERSION }} 73 | RUST_TOOLCHAIN=${{ steps.toolchain.outputs.RUST_TOOLCHAIN }} 74 | WASI_SDK_VERSION_FULL=${{ env.WASI_SDK_VERSION_FULL }} 75 | GCC_VERSION=${{ env.GCC_VERSION }} 76 | -------------------------------------------------------------------------------- /.github/workflows/CreateRelease.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json 2 | name: Create a Release 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: [ release/**, dev ] 7 | 8 | permissions: 9 | contents: write 10 | packages: write 11 | 12 | jobs: 13 | 14 | benchmarks: 15 | uses: ./.github/workflows/Benchmarks.yml 16 | 17 | publish: 18 | needs: [ benchmarks ] 19 | runs-on: windows-latest 20 | strategy: 21 | matrix: 22 | config: [debug, release] 23 | env: 24 | PLATFORM: x64 25 | steps: 26 | - uses: actions/checkout@v4 27 | with: 28 | fetch-depth: 0 29 | # Ensures just is installed using setup wokflow to ensure just version consistency 30 | - name: Hyperlight setup 31 | uses: hyperlight-dev/ci-setup-workflow@v1.4.0 32 | with: 33 | rust-toolchain: "1.82.0" 34 | - name: Install minver_rs 35 | run: | 36 | cargo install minver_rs 37 | shell: pwsh 38 | - name: Set HYPERLIGHTWASM_VERSION 39 | run: | 40 | git fetch --tags || true 41 | $env:MINVER_TAG_PREFIX="v" 42 | $env:MINVER_AUTO_INCREMENT_LEVEL="Minor" 43 | $env:MINVER_PRERELEASE_IDENTIFIER="preview" 44 | echo "HYPERLIGHTWASM_VERSION=$(minver)"| Out-File -FilePath $Env:GITHUB_ENV -Encoding utf-8 -Append 45 | echo "HYPERLIGHTWASM_VERSION=$(minver)" 46 | shell: pwsh 47 | - name: Download Wasm Host 48 | uses: actions/download-artifact@v4 49 | with: 50 | name: wasm-runtime-${{ matrix.config }} 51 | path: ${{ env.PLATFORM }}/${{ matrix.config }} 52 | - name: Download Wasm Modules 53 | uses: actions/download-artifact@v4 54 | with: 55 | name: guest-modules 56 | path: ${{ env.PLATFORM }}/${{ matrix.config }} 57 | - name: Build rust wasm modules 58 | run: just build-rust-wasm-examples ${{ matrix.config }} 59 | shell: bash 60 | - name: Download Benchmarks (Windows) 61 | uses: actions/download-artifact@v4 62 | with: 63 | name: benchmarks_Windows_whp 64 | path: benchmarks_Windows_whp 65 | - name: Download Benchmarks (Linux hyperv) 66 | uses: actions/download-artifact@v4 67 | with: 68 | name: benchmarks_Linux_hyperv 69 | path: benchmarks_Linux_hyperv 70 | - name: Download Benchmarks (Linux kvm) 71 | uses: actions/download-artifact@v4 72 | with: 73 | name: benchmarks_Linux_kvm 74 | path: benchmarks_Linux_kvm 75 | - name: Archive benchmarks 76 | run: | 77 | tar -zcvf benchmarks_Windows_whp.tar.gz benchmarks_Windows_whp 78 | tar -zcvf benchmarks_Linux_hyperv.tar.gz benchmarks_Linux_hyperv 79 | tar -zcvf benchmarks_Linux_kvm.tar.gz benchmarks_Linux_kvm 80 | - name: Install github-cli 81 | run: | 82 | $ProgressPreference = 'SilentlyContinue' 83 | # check if gh cli is installed 84 | $installed = [bool](Get-Command -ErrorAction Ignore -Type Application gh) 85 | if ($installed) { Write-Host "gh cli already installed"; exit 0 } 86 | # download and install gh cli 87 | Invoke-WebRequest https://github.com/cli/cli/releases/download/v2.50.0/gh_2.50.0_windows_amd64.msi -OutFile gh.msi 88 | msiexec.exe /i gh.msi /quiet /l log.txt | Out-Null 89 | Write-Host "msiexec exited with code $LASTEXITCCODE" 90 | if ($LASTEXITCODE -ne 0) { cat log.txt; exit 1 } 91 | - name: Create pre-release 92 | if: (!contains(github.ref, 'refs/heads/release/')) && matrix.config == 'debug' 93 | run: | 94 | echo "PWD: $PWD" 95 | ls -alR 96 | gh release delete dev-latest -y --cleanup-tag || true 97 | gh release create dev-latest -t "Latest Development Build From Dev Branch" --latest=false -p \ 98 | ${{ env.PLATFORM }}/${{ matrix.config }}/HelloWorld.wasm \ 99 | ${{ env.PLATFORM }}/${{ matrix.config }}/RunWasm.wasm \ 100 | ${{ env.PLATFORM }}/${{ matrix.config }}/HelloWorld.aot \ 101 | ${{ env.PLATFORM }}/${{ matrix.config }}/wasm_runtime.* \ 102 | ${{ env.PLATFORM }}/${{ matrix.config }}/rust_wasm_samples.aot \ 103 | ${{ env.PLATFORM }}/${{ matrix.config }}/rust_wasm_samples.wasm \ 104 | benchmarks_Windows_whp.tar.gz \ 105 | benchmarks_Linux_hyperv.tar.gz \ 106 | benchmarks_Linux_kvm.tar.gz 107 | env: 108 | GH_TOKEN: ${{ github.token }} 109 | shell: bash 110 | - name: Create Release 111 | if: contains(github.ref, 'refs/heads/release/') && matrix.config == 'release' 112 | run: | 113 | echo "PWD: $PWD" 114 | ls -alR 115 | gh release create v${{ env.HYPERLIGHTWASM_VERSION }} -t "Release v${{ env.HYPERLIGHTWASM_VERSION }}" --generate-notes \ 116 | ${{ env.PLATFORM }}/${{ matrix.config }}/HelloWorld.wasm \ 117 | ${{ env.PLATFORM }}/${{ matrix.config }}/RunWasm.wasm \ 118 | ${{ env.PLATFORM }}/${{ matrix.config }}/HelloWorld.aot \ 119 | ${{ env.PLATFORM }}/${{ matrix.config }}/RunWasm.aot \ 120 | ${{ env.PLATFORM }}/${{ matrix.config }}/wasm_runtime.* \ 121 | ${{ env.PLATFORM }}/${{ matrix.config }}/rust_wasm_samples.aot \ 122 | ${{ env.PLATFORM }}/${{ matrix.config }}/rust_wasm_samples.wasm \ 123 | benchmarks_Windows_whp.tar.gz \ 124 | benchmarks_Linux_hyperv.tar.gz \ 125 | benchmarks_Linux_kvm.tar.gz 126 | env: 127 | GH_TOKEN: ${{ github.token }} 128 | shell: bash 129 | -------------------------------------------------------------------------------- /.github/workflows/CreateReleaseBranch.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json 2 | # When a new Tag with the prefix v is pushed to the repository, this workflow will create a new release branch called release/. 3 | 4 | name: Create a Release Branch 5 | 6 | on: 7 | push: 8 | tags: 9 | - 'v*' 10 | 11 | permissions: 12 | contents: write # Needed to push new branch 13 | 14 | jobs: 15 | create-branch: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v4 19 | with: 20 | fetch-depth: 0 21 | - name: Create Release Branch 22 | run: | 23 | git checkout -b release/${GITHUB_REF_NAME} 24 | git push --set-upstream origin release/${GITHUB_REF_NAME} 25 | shell: bash 26 | -------------------------------------------------------------------------------- /.github/workflows/IssueLabelChecker.yml: -------------------------------------------------------------------------------- 1 | name: Issue Labeler 2 | on: 3 | issues: 4 | types: [opened] 5 | 6 | permissions: 7 | issues: write 8 | contents: read 9 | 10 | jobs: 11 | labeler: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | - name: Check and Add label 16 | run: | 17 | # The cryptic head -c -1 is because otherwise gh always terminates output with a newline 18 | readarray -d $'\0' lifecycles < <(gh issue view ${{ github.event.issue.number }} --json labels -q '[.labels[] | .name | select(startswith("lifecycle/"))] | join("\u0000")' | head -c -1) 19 | if [[ ${#lifecycles[@]} -ne 1 ]]; then 20 | if [[ ${#lifecycles[@]} -ge 1 ]]; then 21 | echo 'Too many lifecycle labels; replacing all with `lifecycle/needs review`' 22 | fi 23 | commands=() 24 | for label in "${lifecycles[@]}"; do 25 | if [[ "$label" != "lifecycle/needs review" ]]; then 26 | echo "Removing label ${label}" 27 | commands+=("--remove-label" "${label}") 28 | fi 29 | done 30 | echo 'Adding `lifecycle/needs review`' 31 | commands+=("--add-label" "lifecycle/needs review") 32 | gh issue edit ${{ github.event.issue.number }} "${commands[@]}" 33 | fi 34 | env: 35 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/PRLabelChecker.yml: -------------------------------------------------------------------------------- 1 | name: Label Checker 2 | on: 3 | pull_request: 4 | branches: main 5 | types: [opened, labeled, unlabeled, synchronize, reopened] 6 | permissions: 7 | pull-requests: read 8 | contents: read 9 | 10 | jobs: 11 | check-labels: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | - name: Ensure exactly one "kind/*" label is applied 16 | run: | 17 | # Count the number of "kind/*" labels directly from the PR labels 18 | PR_NUMBER=${{ github.event.pull_request.number }} 19 | KIND_LABEL_COUNT=$(gh pr view "$PR_NUMBER" --json labels -q '.labels.[].name' | grep -c '^kind/') 20 | 21 | if [[ "$KIND_LABEL_COUNT" -eq 1 ]]; then 22 | echo "✅ Exactly one 'kind/*' label is applied." 23 | exit 0 24 | else 25 | echo "❌ PR must have exactly one 'kind/*' label, but found $KIND_LABEL_COUNT." 26 | exit 1 27 | fi 28 | env: 29 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 30 | -------------------------------------------------------------------------------- /.github/workflows/README.md: -------------------------------------------------------------------------------- 1 | # GitHub Actions Workflows 2 | 3 | This directory contains [GitHub Workflows](https://docs.github.com/en/actions/using-workflows) of two primary types: 4 | 5 | - Ones to be used as dependencies within other workflow files outside this directory. 6 | - These types of workflows are stored in files with names preceded with `dep_` 7 | - Ones to be executed directly. 8 | 9 | ## More information on dependency workflows 10 | 11 | For more information on how dependencies work in GitHub Actions, see the [GitHub documentation on reusing workflows](https://docs.github.com/en/actions/using-workflows/reusing-workflows). 12 | 13 | ### About the `workflow_call` trigger 14 | 15 | The primary mechanism by which all files within this directory declare themselves dependencies of others is the `workflow_call` trigger. This indicates to GitHub Actions that, for a given workflow, another workflow will invoke it. 16 | 17 | To read more about this trigger, see [GitHub Actions documentation](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#onworkflow_call). 18 | -------------------------------------------------------------------------------- /.github/workflows/ValidatePullRequest.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json 2 | name: Validate Pull Request 3 | 4 | on: 5 | pull_request: 6 | branches: [main, "release/**"] 7 | merge_group: {} 8 | 9 | # Cancels old running job if a new one is triggered (e.g. by a push onto the same branch). 10 | # This will cancel dependent jobs as well, such as dep_rust and dep_fuzzing 11 | concurrency: 12 | group: ${{ github.workflow }}-${{ github.ref }} 13 | cancel-in-progress: true 14 | 15 | permissions: 16 | pull-requests: read 17 | contents: read 18 | packages: read 19 | 20 | jobs: 21 | 22 | docs-pr: 23 | runs-on: ubuntu-latest 24 | outputs: 25 | docs-only: ${{ steps.docs-only.outputs.result }} 26 | steps: 27 | - uses: dorny/paths-filter@v3 28 | id: changes 29 | with: 30 | filters: | 31 | docs: 32 | - '**/*.md' 33 | - '**/*.txt' 34 | all: 35 | - '**/*' 36 | - uses: actions/github-script@v7 37 | id: docs-only 38 | with: 39 | script: | 40 | let docs_file_count = ${{steps.changes.outputs.docs_count}}; 41 | let all_file_count = ${{steps.changes.outputs.all_count}}; 42 | return all_file_count === docs_file_count; 43 | result-encoding: string 44 | 45 | spelling: 46 | name: spell check with typos 47 | runs-on: ubuntu-latest 48 | steps: 49 | - uses: actions/checkout@v4 50 | - name: Spell Check Repo 51 | uses: crate-ci/typos@v1.33.1 52 | 53 | build-wasm-examples: 54 | needs: 55 | - docs-pr 56 | uses: ./.github/workflows/dep_build_wasm_examples.yml 57 | permissions: 58 | contents: read 59 | packages: write # to push the image that builds wasm samples 60 | with: 61 | docs_only: ${{needs.docs-pr.outputs.docs-only}} 62 | 63 | rust: 64 | needs: 65 | - docs-pr 66 | - build-wasm-examples 67 | uses: ./.github/workflows/dep_rust.yml 68 | with: 69 | docs_only: ${{needs.docs-pr.outputs.docs-only}} 70 | 71 | 72 | # Gate PR merges on this specific "join-job" which requires all other 73 | # jobs to run first. We need this job since we cannot gate on particular jobs 74 | # in the workflow, since they can sometimes be skipped (e.g. if the PR only touches docs). 75 | # This step fixes this issue by always running. 76 | report-ci-status: 77 | needs: 78 | - docs-pr 79 | - rust 80 | - spelling 81 | - build-wasm-examples 82 | if: always() 83 | runs-on: ubuntu-latest 84 | steps: 85 | - name: Previous jobs succeeded 86 | if: ${{ !(contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')) }} 87 | run: exit 0 88 | - name: Previous jobs failed 89 | if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }} 90 | run: exit 1 -------------------------------------------------------------------------------- /.github/workflows/auto-merge-dependabot.yml: -------------------------------------------------------------------------------- 1 | name: Auto Merge Dependabot PRs 2 | 3 | on: 4 | schedule: 5 | # Run daily at 04:00 UTC since dependabot runs at 03:00 UTC 6 | - cron: '0 4 * * *' 7 | workflow_dispatch: # Allow manual trigger 8 | 9 | permissions: 10 | contents: write 11 | pull-requests: write 12 | 13 | # This workflow uses a GitHub App token to approve and merge Dependabot PRs 14 | # The token is created using the `actions/create-github-app-token` action 15 | # The token is used so that the updates are made by the GitHub App instead of Github Actions 16 | # and will show up as such in the PR comments and history 17 | # In addition, the token is scoped to only the permissions needed for this workflow 18 | # see https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/making-authenticated-api-requests-with-a-github-app-in-a-github-actions-workflow for details 19 | 20 | jobs: 21 | auto-merge-dependabot: 22 | runs-on: ubuntu-latest 23 | steps: 24 | 25 | # Gets the GitHub App token 26 | - uses: actions/create-github-app-token@v2 27 | id: get-app-token 28 | with: 29 | # required 30 | app-id: ${{ secrets.DEPENDABOT_APP_ID }} 31 | private-key: ${{ secrets.DEPENDABOT_APP_KEY }} 32 | permission-pull-requests: write 33 | permission-contents: write 34 | 35 | - name: Checkout code 36 | uses: actions/checkout@v4 37 | with: 38 | token: ${{ steps.get-app-token.outputs.token }} 39 | persist-credentials: false 40 | 41 | - name: Setup GitHub CLI 42 | run: | 43 | # GitHub CLI is pre-installed on GitHub-hosted runners 44 | gh --version 45 | 46 | - name: Run auto approve script 47 | env: 48 | GITHUB_TOKEN: ${{ steps.get-app-token.outputs.token }} 49 | run: ./src/scripts/auto-approve-dependabot.sh ${{ github.repository }} 50 | -------------------------------------------------------------------------------- /.github/workflows/dep_build_wasm_examples.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json 2 | # See README.md in this directory for more information about workflow_call 3 | 4 | name: Build Wasm Examples 5 | 6 | on: 7 | workflow_dispatch: {} 8 | workflow_call: 9 | inputs: 10 | docs_only: 11 | description: Skip building if docs only 12 | required: false 13 | type: string 14 | default: "false" 15 | schedule: 16 | # Run at 1am UTC daily 17 | - cron: '0 1 * * *' 18 | 19 | permissions: 20 | packages: write 21 | contents: read 22 | 23 | jobs: 24 | build-wasm-examples: 25 | if: ${{ inputs.docs_only == 'false' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' }} 26 | runs-on: ubuntu-latest 27 | steps: 28 | - uses: actions/checkout@v4 29 | with: 30 | fetch-depth: 0 31 | - name: Remove default clang 32 | run: sudo rm /usr/bin/clang 33 | - name: Hyperlight setup workflow 34 | uses: hyperlight-dev/ci-setup-workflow@v1.4.0 35 | with: 36 | rust-toolchain: "1.82.0" 37 | - name: Set up Docker Buildx 38 | uses: docker/setup-buildx-action@v3 39 | - name: Login to Registry 40 | uses: docker/login-action@v3 41 | with: 42 | registry: ghcr.io 43 | username: ${{ github.actor }} 44 | password: ${{ secrets.GITHUB_TOKEN }} 45 | - name: Pull wasm-clang-builder 46 | continue-on-error: true 47 | run: | 48 | docker pull ghcr.io/${{ github.repository_owner }}/wasm-clang-builder:latest 49 | - name: Set up Docker image metadata 50 | id: meta 51 | uses: docker/metadata-action@v5 52 | with: 53 | images: ${{ github.repository_owner }}/wasm-clang-builder 54 | - name: Build and push wasm-clang-builder 55 | # Only push if not from a fork, not from pull request, and not from dependabot 56 | uses: docker/build-push-action@v6 57 | with: 58 | context: src/wasmsamples 59 | file: src/wasmsamples/dockerfile 60 | load: true 61 | push: ${{ (github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false) && github.actor != 'dependabot[bot]' }} 62 | build-args: | 63 | WASI_SDK_VERSION_FULL=20.0 64 | GCC_VERSION=12 65 | tags: ghcr.io/${{ github.repository_owner }}/wasm-clang-builder:latest 66 | cache-from: type=registry,ref=ghcr.io/${{ github.repository_owner }}/wasm-clang-builder:buildcache 67 | cache-to: type=registry,ref=ghcr.io/${{ github.repository_owner }}/wasm-clang-builder:buildcache,mode=max 68 | - name: Build Modules 69 | run: | 70 | for FILENAME in $(find . -name '*.c') 71 | do 72 | echo Building ${FILENAME} 73 | docker run --rm -i -v "${PWD}:/tmp/host" ghcr.io/${{ github.repository_owner }}/wasm-clang-builder:latest /opt/wasi-sdk/bin/clang -flto -ffunction-sections -mexec-model=reactor -O3 -z stack-size=4096 -Wl,--initial-memory=65536 -Wl,--export=__data_end -Wl,--export=__heap_base,--export=malloc,--export=free,--export=__wasm_call_ctors -Wl,--strip-all,--no-entry -Wl,--allow-undefined -Wl,--gc-sections -o /tmp/host/${FILENAME%.*}-wasi-libc.wasm /tmp/host/${FILENAME} 74 | cargo run -p hyperlight-wasm-aot compile ${FILENAME%.*}-wasi-libc.wasm ${FILENAME%.*}.aot 75 | cp ${FILENAME%.*}.aot ${FILENAME%.*}.wasm 76 | done 77 | shell: bash 78 | working-directory: src/wasmsamples 79 | - name: Upload Wasm Modules 80 | uses: actions/upload-artifact@v4 81 | with: 82 | name: guest-modules 83 | path: | 84 | src/wasmsamples/*.wasm 85 | src/wasmsamples/*.aot 86 | -------------------------------------------------------------------------------- /.github/workflows/dep_cargo_publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish Crates to Cargo Registry 2 | 3 | on: 4 | workflow_call: 5 | inputs: 6 | event_name: 7 | description: 'The event that triggered the workflow. (pull_request, workflow_dispatch, workflow_call, push)' 8 | type: string 9 | required: true 10 | default: 'pull_request' 11 | 12 | jobs: 13 | publish-hyperlight-wasm: 14 | runs-on: [self-hosted, Linux, X64, "1ES.Pool=HL-Ubuntu-22.04-KVM"] 15 | steps: 16 | 17 | - uses: actions/checkout@v4 18 | with: 19 | fetch-depth: 0 20 | fetch-tags: true 21 | 22 | - name: Hyperlight setup 23 | uses: hyperlight-dev/ci-setup-workflow@v1.4.0 24 | with: 25 | rust-toolchain: "1.82.0" 26 | 27 | - name: Download Wasm Host (debug) 28 | uses: actions/download-artifact@v4 29 | with: 30 | name: wasm-runtime-debug 31 | path: ${{ github.workspace }}/src/hyperlight_wasm/redist/debug/ 32 | 33 | - name: Download Wasm Host (release) 34 | uses: actions/download-artifact@v4 35 | with: 36 | name: wasm-runtime-release 37 | path: ${{ github.workspace }}/src/hyperlight_wasm/redist/release/ 38 | 39 | # github actions that run against PRs check out a ref to the PR merge branch 40 | # we need to switch / create a branch for cargo ws to run late 41 | - name: set-branch-for-PRs 42 | if: ${{ inputs.event_name == 'pull_request' }} 43 | run: | 44 | git switch -c ${{ github.ref}} 45 | 46 | - name: Setup minver and cargo-workspaces 47 | run: | 48 | cargo install cargo-workspaces 49 | cargo install minver_rs 50 | git config --global user.email "${{ github.actor }}@users.noreply.github.com" 51 | git config --global user.Name "${{ github.actor }}" 52 | 53 | - name: Set crate versions 54 | run: | 55 | git fetch --tags || true 56 | version=$(MINVER_TAG_PREFIX=v MINVER_AUTO_INCREMENT_LEVEL=Minor MINVER_PRERELEASE_IDENTIFIER=preview minver) 57 | echo "Setting version to $version" 58 | cargo ws version --force=hyperlight_* --no-git-commit --yes custom $version 59 | echo "HYPERLIGHTWASM_VERSION=$version" >> "$GITHUB_ENV" 60 | 61 | - name: Determine if we should publish crates 62 | run: | 63 | echo "github.ref=${{ github.ref }}" 64 | echo "HYPERLIGHTWASM_VERSION=$HYPERLIGHTWASM_VERSION" 65 | if [[ ${{ github.ref }} =~ 'refs/heads/release/' || ( ${{ github.ref }} == 'refs/heads/dev' && $HYPERLIGHTWASM_VERSION =~ '-preview' ) ]] 66 | then 67 | echo "Setting SHOULD_PUBLISH in GITHUB_ENV" 68 | echo "SHOULD_PUBLISH=true" >> "$GITHUB_ENV" 69 | fi 70 | 71 | - name: list package contents 72 | working-directory: src/hyperlight_wasm 73 | run: cargo package --list --manifest-path ./Cargo.toml --allow-dirty 74 | # allow-dirty is needed in the publish below because we are using the --no-git-commit option above to cover the case 75 | # where no changes are made by cargo ws version because the version is already correct 76 | 77 | # Run cargo package to perform publishing validation if this workflow 78 | # is triggered by a pull-request 79 | - name: Package hyperlight-wasm (dry-run) 80 | if: ${{ inputs.event_name == 'pull_request' }} 81 | working-directory: src/hyperlight_wasm 82 | run: cargo package --manifest-path ./Cargo.toml --allow-dirty -------------------------------------------------------------------------------- /.github/workflows/dep_rust.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json 2 | name: Rust Tests and Lints 3 | 4 | # See README.md in this directory for more information about workflow_call 5 | on: 6 | workflow_call: 7 | inputs: 8 | docs_only: 9 | description: Skip building if docs only 10 | required: false 11 | type: string 12 | default: "false" 13 | 14 | permissions: 15 | contents: read 16 | 17 | # The reason for default shell bash is because on our self-hosted windows runners, 18 | # the default shell is powershell, which doesn't work correctly together with `just` commands. 19 | # Even if a command inside a just-recipe fails, github reports the step as successful. 20 | # The problem may or may not be related to our custom windows runner not applying the 21 | # powershell steps outlined here 22 | # https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#exit-codes-and-error-action-preference 23 | defaults: 24 | run: 25 | shell: bash 26 | 27 | env: 28 | CARGO_TERM_COLOR: always 29 | RUST_BACKTRACE: full 30 | 31 | jobs: 32 | # this job requires the build-guest-binaries job be complete prior to 33 | # its execution. this dependency should be expressed in the dependent 34 | # workflow 35 | build: 36 | if: ${{ inputs.docs_only == 'false' }} 37 | strategy: 38 | fail-fast: true 39 | matrix: 40 | hypervisor: [hyperv, mshv, mshv3, kvm] # hyperv is windows, mshv and kvm are linux 41 | cpu: [amd, intel] 42 | config: [debug, release] 43 | 44 | runs-on: ${{ fromJson( 45 | format('["self-hosted", "{0}", "X64", "1ES.Pool=hld-{1}-{2}"]', 46 | matrix.hypervisor == 'hyperv' && 'Windows' || 'Linux', 47 | matrix.hypervisor == 'hyperv' && 'win2022' || matrix.hypervisor == 'mshv3' && 'azlinux3-mshv' || matrix.hypervisor, 48 | matrix.cpu)) }} 49 | steps: 50 | - uses: actions/checkout@v4 51 | 52 | - name: Hyperlight setup 53 | uses: hyperlight-dev/ci-setup-workflow@v1.4.0 54 | with: 55 | rust-toolchain: "1.82.0" 56 | 57 | - name: Add Nightly Rust 58 | run: | 59 | rustup toolchain install nightly 60 | for target in $(rustup target list --installed); do 61 | rustup target add $target --toolchain nightly 62 | done 63 | rustup target add wasm32-unknown-unknown 64 | shell: bash 65 | 66 | - name: Add Nightly Rust Windows 67 | if: runner.os == 'Windows' 68 | run: rustup component add --toolchain nightly-x86_64-pc-windows-msvc rustfmt 69 | 70 | - name: Build Wasm Runtime Binary 71 | working-directory: ./src/hyperlight_wasm 72 | run: just build-wasm-runtime ${{ matrix.config }} 73 | 74 | - name: Download Wasm Modules 75 | uses: actions/download-artifact@v4 76 | with: 77 | name: guest-modules 78 | path: ./x64/${{ matrix.config }} 79 | 80 | - name: Fmt 81 | run: just fmt-check 82 | 83 | - name: Clippy 84 | run: just clippy ${{ matrix.config }} 85 | 86 | - name: Build 87 | run: just build ${{ matrix.config }} ${{ matrix.hypervisor == 'mshv3' && 'mshv3' || ''}} 88 | working-directory: ./src/hyperlight_wasm 89 | 90 | - name: Build Rust Wasm examples 91 | run: just build-rust-wasm-examples ${{ matrix.config }} 92 | working-directory: ./src/hyperlight_wasm 93 | 94 | - name: Test 95 | run: just test ${{ matrix.config }} ${{ matrix.hypervisor == 'mshv3' && 'mshv3' || ''}} 96 | working-directory: ./src/hyperlight_wasm 97 | 98 | - name: Install github-cli (Windows) 99 | if: runner.os == 'Windows' 100 | run: | 101 | $ProgressPreference = 'SilentlyContinue' 102 | # check if gh cli is installed 103 | $installed = [bool](Get-Command -ErrorAction Ignore -Type Application gh) 104 | if ($installed) { Write-Host "gh cli already installed"; exit 0 } 105 | # download and install gh cli 106 | Invoke-WebRequest https://github.com/cli/cli/releases/download/v2.68.1/gh_2.68.1_windows_amd64.msi -OutFile gh.msi 107 | msiexec.exe /i gh.msi /quiet /l log.txt | Out-Null 108 | Write-Host "msiexec exited with code $LASTEXITCCODE" 109 | if ($LASTEXITCODE -ne 0) { cat log.txt; exit 1 } 110 | shell: pwsh 111 | 112 | - name: Install github-cli (Linux mariner) 113 | if: runner.os == 'Linux' && (matrix.hypervisor == 'mshv2' || matrix.hypervisor == 'mshv3') 114 | run: sudo dnf install gh -y 115 | 116 | - name: Install github-cli (Linux ubuntu) 117 | if: runner.os == 'Linux' && matrix.hypervisor == 'kvm' 118 | run: sudo apt install gh -y 119 | 120 | - name: Test Examples 121 | run: just examples-ci ${{ matrix.config }} ${{ matrix.hypervisor == 'mshv3' && 'mshv3' || ''}} 122 | working-directory: ./src/hyperlight_wasm 123 | env: 124 | # required for gh cli when downloading 125 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 126 | 127 | ### Benchmarks ### 128 | 129 | - name: Download benchmarks from "latest" 130 | run: | 131 | just bench-download ${{ runner.os }} ${{ matrix.hypervisor }} dev-latest 132 | env: 133 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 134 | continue-on-error: true 135 | working-directory: ./src/hyperlight_wasm 136 | if: ${{ matrix.config == 'release' }} 137 | 138 | - name: Run benchmarks 139 | run: | 140 | just bench-ci dev ${{ matrix.config }} ${{ matrix.hypervisor == 'mshv3' && 'mshv3' || ''}} 141 | working-directory: ./src/hyperlight_wasm 142 | if: ${{ matrix.config == 'release' }} 143 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Ll]og/ 33 | [Ll]ogs/ 34 | 35 | # Visual Studio 2015/2017 cache/options directory 36 | .vs/ 37 | # Uncomment if you have tasks that create the project's static files in wwwroot 38 | #wwwroot/ 39 | 40 | # Visual Studio 2017 auto generated files 41 | Generated\ Files/ 42 | 43 | # MSTest test Results 44 | [Tt]est[Rr]esult*/ 45 | [Bb]uild[Ll]og.* 46 | 47 | # NUnit 48 | *.VisualState.xml 49 | TestResult.xml 50 | nunit-*.xml 51 | 52 | # Build Results of an ATL Project 53 | [Dd]ebugPS/ 54 | [Rr]eleasePS/ 55 | dlldata.c 56 | 57 | # Benchmark Results 58 | BenchmarkDotNet.Artifacts/ 59 | 60 | # .NET Core 61 | project.lock.json 62 | project.fragment.lock.json 63 | artifacts/ 64 | 65 | # Tye 66 | .tye/ 67 | 68 | # ASP.NET Scaffolding 69 | ScaffoldingReadMe.txt 70 | 71 | # StyleCop 72 | StyleCopReport.xml 73 | 74 | # Files built by Visual Studio 75 | *_i.c 76 | *_p.c 77 | *_h.h 78 | *.ilk 79 | *.meta 80 | *.obj 81 | *.iobj 82 | *.pch 83 | *.pdb 84 | *.ipdb 85 | *.pgc 86 | *.pgd 87 | *.rsp 88 | *.sbr 89 | *.tlb 90 | *.tli 91 | *.tlh 92 | *.tmp 93 | *.tmp_proj 94 | *_wpftmp.csproj 95 | *.log 96 | *.vspscc 97 | *.vssscc 98 | .builds 99 | *.pidb 100 | *.svclog 101 | *.scc 102 | 103 | # Chutzpah Test files 104 | _Chutzpah* 105 | 106 | # Visual C++ cache files 107 | ipch/ 108 | *.aps 109 | *.ncb 110 | *.opendb 111 | *.opensdf 112 | *.sdf 113 | *.cachefile 114 | *.VC.db 115 | *.VC.VC.opendb 116 | 117 | # Visual Studio profiler 118 | *.psess 119 | *.vsp 120 | *.vspx 121 | *.sap 122 | 123 | # Visual Studio Trace Files 124 | *.e2e 125 | 126 | # TFS 2012 Local Workspace 127 | $tf/ 128 | 129 | # Guidance Automation Toolkit 130 | *.gpState 131 | 132 | # ReSharper is a .NET coding add-in 133 | _ReSharper*/ 134 | *.[Rr]e[Ss]harper 135 | *.DotSettings.user 136 | 137 | # TeamCity is a build add-in 138 | _TeamCity* 139 | 140 | # DotCover is a Code Coverage Tool 141 | *.dotCover 142 | 143 | # AxoCover is a Code Coverage Tool 144 | .axoCover/* 145 | !.axoCover/settings.json 146 | 147 | # Coverlet is a free, cross platform Code Coverage Tool 148 | coverage*.json 149 | coverage*.xml 150 | coverage*.info 151 | 152 | # Visual Studio code coverage results 153 | *.coverage 154 | *.coveragexml 155 | 156 | # NCrunch 157 | _NCrunch_* 158 | .*crunch*.local.xml 159 | nCrunchTemp_* 160 | 161 | # MightyMoose 162 | *.mm.* 163 | AutoTest.Net/ 164 | 165 | # Web workbench (sass) 166 | .sass-cache/ 167 | 168 | # Installshield output folder 169 | [Ee]xpress/ 170 | 171 | # DocProject is a documentation generator add-in 172 | DocProject/buildhelp/ 173 | DocProject/Help/*.HxT 174 | DocProject/Help/*.HxC 175 | DocProject/Help/*.hhc 176 | DocProject/Help/*.hhk 177 | DocProject/Help/*.hhp 178 | DocProject/Help/Html2 179 | DocProject/Help/html 180 | 181 | # Click-Once directory 182 | publish/ 183 | 184 | # Publish Web Output 185 | *.[Pp]ublish.xml 186 | *.azurePubxml 187 | # Note: Comment the next line if you want to checkin your web deploy settings, 188 | # but database connection strings (with potential passwords) will be unencrypted 189 | *.pubxml 190 | *.publishproj 191 | 192 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 193 | # checkin your Azure Web App publish settings, but sensitive information contained 194 | # in these scripts will be unencrypted 195 | PublishScripts/ 196 | 197 | # NuGet Packages 198 | *.nupkg 199 | # NuGet Symbol Packages 200 | *.snupkg 201 | # The packages folder can be ignored because of Package Restore 202 | **/[Pp]ackages/* 203 | # except build/, which is used as an MSBuild target. 204 | !**/[Pp]ackages/build/ 205 | # Uncomment if necessary however generally it will be regenerated when needed 206 | #!**/[Pp]ackages/repositories.config 207 | # NuGet v3's project.json files produces more ignorable files 208 | *.nuget.props 209 | *.nuget.targets 210 | 211 | # Microsoft Azure Build Output 212 | csx/ 213 | *.build.csdef 214 | 215 | # Microsoft Azure Emulator 216 | ecf/ 217 | rcf/ 218 | 219 | # Windows Store app package directories and files 220 | AppPackages/ 221 | BundleArtifacts/ 222 | Package.StoreAssociation.xml 223 | _pkginfo.txt 224 | *.appx 225 | *.appxbundle 226 | *.appxupload 227 | 228 | # Visual Studio cache files 229 | # files ending in .cache can be ignored 230 | *.[Cc]ache 231 | # but keep track of directories ending in .cache 232 | !?*.[Cc]ache/ 233 | 234 | # Others 235 | ClientBin/ 236 | ~$* 237 | *~ 238 | *.dbmdl 239 | *.dbproj.schemaview 240 | *.jfm 241 | *.pfx 242 | *.publishsettings 243 | orleans.codegen.cs 244 | 245 | # Including strong name files can present a security risk 246 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 247 | #*.snk 248 | 249 | # Since there are multiple workflows, uncomment next line to ignore bower_components 250 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 251 | #bower_components/ 252 | 253 | # RIA/Silverlight projects 254 | Generated_Code/ 255 | 256 | # Backup & report files from converting an old project file 257 | # to a newer Visual Studio version. Backup files are not needed, 258 | # because we have git ;-) 259 | _UpgradeReport_Files/ 260 | Backup*/ 261 | UpgradeLog*.XML 262 | UpgradeLog*.htm 263 | ServiceFabricBackup/ 264 | *.rptproj.bak 265 | 266 | # SQL Server files 267 | *.mdf 268 | *.ldf 269 | *.ndf 270 | 271 | # Business Intelligence projects 272 | *.rdl.data 273 | *.bim.layout 274 | *.bim_*.settings 275 | *.rptproj.rsuser 276 | *- [Bb]ackup.rdl 277 | *- [Bb]ackup ([0-9]).rdl 278 | *- [Bb]ackup ([0-9][0-9]).rdl 279 | 280 | # Microsoft Fakes 281 | FakesAssemblies/ 282 | 283 | # GhostDoc plugin setting file 284 | *.GhostDoc.xml 285 | 286 | # Node.js Tools for Visual Studio 287 | .ntvs_analysis.dat 288 | node_modules/ 289 | 290 | # Visual Studio 6 build log 291 | *.plg 292 | 293 | # Visual Studio 6 workspace options file 294 | *.opt 295 | 296 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 297 | *.vbw 298 | 299 | # Visual Studio LightSwitch build output 300 | **/*.HTMLClient/GeneratedArtifacts 301 | **/*.DesktopClient/GeneratedArtifacts 302 | **/*.DesktopClient/ModelManifest.xml 303 | **/*.Server/GeneratedArtifacts 304 | **/*.Server/ModelManifest.xml 305 | _Pvt_Extensions 306 | 307 | # Paket dependency manager 308 | .paket/paket.exe 309 | paket-files/ 310 | 311 | # FAKE - F# Make 312 | .fake/ 313 | 314 | # CodeRush personal settings 315 | .cr/personal 316 | 317 | # Python Tools for Visual Studio (PTVS) 318 | __pycache__/ 319 | *.pyc 320 | 321 | # Cake - Uncomment if you are using it 322 | # tools/** 323 | # !tools/packages.config 324 | 325 | # Tabs Studio 326 | *.tss 327 | 328 | # Telerik's JustMock configuration file 329 | *.jmconfig 330 | 331 | # BizTalk build output 332 | *.btp.cs 333 | *.btm.cs 334 | *.odx.cs 335 | *.xsd.cs 336 | 337 | # OpenCover UI analysis results 338 | OpenCover/ 339 | 340 | # Azure Stream Analytics local run output 341 | ASALocalRun/ 342 | 343 | # MSBuild Binary and Structured Log 344 | *.binlog 345 | 346 | # NVidia Nsight GPU debugger configuration file 347 | *.nvuser 348 | 349 | # MFractors (Xamarin productivity tool) working folder 350 | .mfractor/ 351 | 352 | # Local History for Visual Studio 353 | .localhistory/ 354 | 355 | # BeatPulse healthcheck temp database 356 | healthchecksdb 357 | 358 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 359 | MigrationBackup/ 360 | 361 | # Ionide (cross platform F# VS Code tools) working folder 362 | .ionide/ 363 | 364 | # Fody - auto-generated XML schema 365 | FodyWeavers.xsd 366 | 367 | ## 368 | ## Visual studio for Mac 369 | ## 370 | 371 | 372 | # globs 373 | Makefile.in 374 | *.userprefs 375 | *.usertasks 376 | config.make 377 | config.status 378 | aclocal.m4 379 | install-sh 380 | autom4te.cache/ 381 | *.tar.gz 382 | tarballs/ 383 | test-results/ 384 | 385 | # Mac bundle stuff 386 | *.dmg 387 | *.app 388 | 389 | # content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore 390 | # General 391 | .DS_Store 392 | .AppleDouble 393 | .LSOverride 394 | 395 | # Icon must end with two \r 396 | Icon 397 | 398 | 399 | # Thumbnails 400 | ._* 401 | 402 | # Files that might appear in the root of a volume 403 | .DocumentRevisions-V100 404 | .fseventsd 405 | .Spotlight-V100 406 | .TemporaryItems 407 | .Trashes 408 | .VolumeIcon.icns 409 | .com.apple.timemachine.donotpresent 410 | 411 | # Directories potentially created on remote AFP share 412 | .AppleDB 413 | .AppleDesktop 414 | Network Trash Folder 415 | Temporary Items 416 | .apdisk 417 | 418 | # content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore 419 | # Windows thumbnail cache files 420 | Thumbs.db 421 | ehthumbs.db 422 | ehthumbs_vista.db 423 | 424 | # Dump file 425 | *.stackdump 426 | 427 | # Folder config file 428 | [Dd]esktop.ini 429 | 430 | # Recycle Bin used on file shares 431 | $RECYCLE.BIN/ 432 | 433 | # Windows Installer files 434 | *.cab 435 | *.msi 436 | *.msix 437 | *.msm 438 | *.msp 439 | 440 | # Windows shortcuts 441 | *.lnk 442 | 443 | # JetBrains Rider 444 | .idea/ 445 | *.sln.iml 446 | 447 | ## 448 | ## Visual Studio Code 449 | ## 450 | .vscode/* 451 | !.vscode/settings.json 452 | !.vscode/tasks.json 453 | !.vscode/launch.json 454 | !.vscode/extensions.json 455 | 456 | 457 | ## Hyperlight 458 | 459 | **/include/hyperlight*.h 460 | **/include/printf.h 461 | **/native/HyperlightGuest.lib 462 | **/native/HyperlightGuest.pdb 463 | **/include/libc/musl/** 464 | **/include/flatbuffers/generated/** 465 | **/include/flatcc/** 466 | **/guest-toolchain 467 | src/wasm_runtime/src/include 468 | 469 | ## Rust from https://github.com/github/gitignore/blob/main/Rust.gitignore 470 | 471 | # Generated by Cargo 472 | # will have compiled files and executables 473 | debug/ 474 | target/ 475 | 476 | # These are backup files generated by rustfmt 477 | **/*.rs.bk 478 | 479 | # MSVC Windows builds of rustc generate these, which store debugging information 480 | *.pdb 481 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | // this can be used to debug tests 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "type": "lldb", 10 | "request": "launch", 11 | "name": "Cargo test", 12 | "cargo": { 13 | "args": [ 14 | "test", 15 | "--profile=dev", 16 | "--lib", 17 | "--no-run" 18 | 19 | ], 20 | "filter": { 21 | "name": "hyperlight_wasm", 22 | "kind": "lib" 23 | } 24 | }, 25 | "env": { 26 | "RUST_DIR_FOR_DEBUGGING_TESTS": "${workspaceFolder}/src/hyperlight_wasm" 27 | }, 28 | "args": [ 29 | "--exact", 30 | "sandbox::loaded_wasm_sandbox::tests::test_call_host_func_with_vecbytes" 31 | ] 32 | } 33 | ] 34 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | // This is needed to allow tests to find files when running under the debugger 4 | "rust-analyzer.runnables.extraEnv": { 5 | "RUST_DIR_FOR_DEBUGGING_TESTS": "${workspaceFolder}/src/hyperlight_wasm" 6 | } 7 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). 4 | 5 | ## [Prerelease] - Unreleased 6 | 7 | The Initial hyperlight-wasm Release 🎉 8 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | - Employees can reach out at [aka.ms/opensource/moderation-support](https://aka.ms/opensource/moderation-support) 11 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | This project welcomes contributions. Most contributions require you to 4 | signoff on your commits via the Developer Certificate of Origin 5 | (DCO). When you submit a pull request, a DCO-bot will automatically 6 | determine whether you need to provide signoff for your commit. Please 7 | follow the instructions provided by DCO-bot, as pull requests cannot 8 | be merged until the author(s) have provided signoff to fulfill the DCO 9 | requirement. You may find more information on the DCO requirements 10 | [below](#developer-certificate-of-origin-signing-your-work). 11 | 12 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 13 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 14 | or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 15 | 16 | ## Issues 17 | 18 | This section describes the guidelines for submitting issues 19 | 20 | ### Issue Types 21 | 22 | There are 2 types of issues: 23 | 24 | - Bug: You've found a bug with the code, and want to report it, or create an issue to track the bug. 25 | - Proposal: Used for items that propose a new idea or functionality. This allows feedback from others before code is written. 26 | 27 | ## Contributing to Hyperlight 28 | 29 | This section describes the guidelines for contributing code / docs to Hyperlight. 30 | 31 | ### Pull Requests 32 | 33 | All contributions come through pull requests. To submit a proposed change, we recommend following this workflow: 34 | 35 | 1. Make sure there's an issue (bug or proposal) raised, which sets the expectations for the contribution you are about to make. 36 | 2. Fork the relevant repo and create a new branch 37 | 3. Create your change 38 | - Code changes require tests 39 | - Make sure to run the linters to check and format the code 40 | 4. Update relevant documentation for the change 41 | 5. Commit with [DCO sign-off](#developer-certificate-of-origin-signing-your-work) and open a PR 42 | 6. Wait for the CI process to finish and make sure all checks are green 43 | 7. A maintainer of the project will be assigned, and you can expect a review within a few days 44 | 45 | #### Use work-in-progress PRs for early feedback 46 | 47 | A good way to communicate before investing too much time is to create a "Work-in-progress" PR and share it with your reviewers. The standard way of doing this is to add a "[WIP]" prefix in your PR's title and open the pull request as a draft. 48 | 49 | ### Developer Certificate of Origin: Signing your work 50 | 51 | #### Every commit needs to be signed-off 52 | 53 | The Developer Certificate of Origin (DCO) is a lightweight way for contributors to certify that they wrote or otherwise have the right to submit the code they are contributing to the project. Here is the full text of the [DCO](https://developercertificate.org/), reformatted for readability: 54 | ``` 55 | By making a contribution to this project, I certify that: 56 | 57 | (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or 58 | 59 | (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or 60 | 61 | (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it. 62 | 63 | (d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved. 64 | ``` 65 | 66 | Contributors sign-off that they adhere to these requirements by adding a `Signed-off-by` line to commit messages. 67 | 68 | ```text 69 | This is my commit message 70 | 71 | Signed-off-by: Random J Developer 72 | ``` 73 | 74 | Git even has a `-s` command line option to append this automatically to your commit message: 75 | 76 | ```sh 77 | git commit -s -m 'This is my commit message' 78 | ``` 79 | 80 | Each Pull Request is checked whether or not commits in a Pull Request do contain a valid Signed-off-by line. 81 | 82 | #### I didn't sign my commit, now what?! 83 | 84 | No worries - You can easily replay your changes, sign them and force push them! 85 | 86 | ```sh 87 | git checkout 88 | git commit --amend --no-edit --signoff 89 | git push --force-with-lease 90 | ``` 91 | 92 | *Credit: This doc was cribbed from Dapr.* 93 | 94 | ### Rust Analyzer 95 | 96 | If you are using the [Rust Analyzer](https://rust-analyzer.github.io/manual.html) then you may need to set the configuration option `rust-analyzer.rustfmt.extraArgs` to `["+nightly"]` to ensure that formatting works correctly as this project has a [`rustfmt.toml`](./rustfmt.toml) file that uses nightly features. 97 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["src/hyperlight_wasm", "src/examples_common", "src/hyperlight_wasm_aot" ] 3 | exclude = [ "src/wasm_runtime", "src/rust_wasm_samples", "src/hyperlight_wasm_macro" ] 4 | resolver = "2" 5 | 6 | [workspace.dependencies] 7 | hyperlight-host = { git = "https://github.com/hyperlight-dev/hyperlight", branch = "hyperlight-component-macro", default-features = false, features = ["executable_heap"] } 8 | -------------------------------------------------------------------------------- /Justfile: -------------------------------------------------------------------------------- 1 | default-target:= "debug" 2 | default-tag:= "latest" 3 | build-wasm-examples-command := if os() == "windows" { "./src/hyperlight_wasm/scripts/build-wasm-examples.bat" } else { "./src/hyperlight_wasm/scripts/build-wasm-examples.sh" } 4 | mkdir-arg := if os() == "windows" { "-Force" } else { "-p" } 5 | latest-release:= if os() == "windows" {"$(git tag -l --sort=v:refname | select -last 2 | select -first 1)"} else {`git tag -l --sort=v:refname | tail -n 2 | head -n 1`} 6 | 7 | set windows-shell := ["pwsh.exe", "-NoLogo", "-Command"] 8 | 9 | build-all target=default-target: (build target) (build-wasm-examples target) (build-rust-wasm-examples target) (build-wasm-runtime target) 10 | 11 | build target=default-target features="": (build-wasm-runtime target) (fmt-check) 12 | cargo build {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --verbose --profile={{ if target == "debug" {"dev"} else { target } }} 13 | 14 | mkdir-redist target=default-target: 15 | mkdir {{ mkdir-arg }} x64 16 | mkdir {{ mkdir-arg }} x64/{{ target }} 17 | mkdir {{ mkdir-arg }} src/hyperlight_wasm/redist 18 | mkdir {{ mkdir-arg }} src/hyperlight_wasm/redist/{{ target }} 19 | 20 | build-wasm-runtime target=default-target: (mkdir-redist target) 21 | cd ./src/wasm_runtime && cargo build --verbose --profile={{ if target == "debug" {"dev"} else { target } }} 22 | cp ./src/wasm_runtime/target/x86_64-unknown-none/{{target}}/wasm_runtime ./x64/{{target}}/wasm_runtime 23 | cp ./src/wasm_runtime/target/x86_64-unknown-none/{{target}}/wasm_runtime ./src/hyperlight_wasm/redist/{{target}}/wasm_runtime 24 | 25 | build-wasm-examples target=default-target: 26 | {{ build-wasm-examples-command}} {{target}} 27 | 28 | build-rust-wasm-examples target=default-target: (mkdir-redist target) 29 | rustup target add wasm32-unknown-unknown 30 | cd ./src/rust_wasm_samples && cargo build --target wasm32-unknown-unknown --profile={{ if target == "debug" {"dev"} else { target } }} 31 | cargo run -p hyperlight-wasm-aot compile ./src/rust_wasm_samples/target/wasm32-unknown-unknown/{{ target }}/rust_wasm_samples.wasm ./x64/{{ target }}/rust_wasm_samples.aot 32 | cp ./x64/{{ target }}/rust_wasm_samples.aot ./x64/{{ target }}/rust_wasm_samples.wasm 33 | 34 | check target=default-target: 35 | cargo check --profile={{ if target == "debug" {"dev"} else { target } }} 36 | cd src/rust_wasm_samples && cargo check --profile={{ if target == "debug" {"dev"} else { target } }} 37 | cd src/wasm_runtime && cargo check --profile={{ if target == "debug" {"dev"} else { target } }} 38 | 39 | fmt-check: 40 | rustup toolchain install nightly -c rustfmt && cargo +nightly fmt -v --all -- --check 41 | cd src/rust_wasm_samples && rustup toolchain install nightly -c rustfmt && cargo +nightly fmt -v --all -- --check 42 | cd src/wasm_runtime && rustup toolchain install nightly -c rustfmt && cargo +nightly fmt -v --all -- --check 43 | fmt: 44 | cargo +nightly fmt --all 45 | cd src/rust_wasm_samples && cargo +nightly fmt 46 | cd src/wasm_runtime && cargo +nightly fmt 47 | 48 | clippy target=default-target: (check target) 49 | cargo clippy --profile={{ if target == "debug" {"dev"} else { target } }} --all-targets --all-features -- -D warnings 50 | cd src/rust_wasm_samples && cargo clippy --profile={{ if target == "debug" {"dev"} else { target } }} --all-targets --all-features -- -D warnings 51 | cd src/wasm_runtime && cargo clippy --profile={{ if target == "debug" {"dev"} else { target } }} --all-targets --all-features -- -D warnings 52 | 53 | # TESTING 54 | # Metrics tests cannot run with other tests they are marked as ignored so that cargo test works 55 | # There may be tests that we really want to ignore so we cant just use --ignored and run then we have to 56 | # specify the test name of the ignored tests that we want to run 57 | # Additionally, we have to run the tests with the function_call_metrics feature enabled separately 58 | test target=default-target features="": (test-inprocess target) (test-seccomp target features) 59 | cargo test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" {"dev"} else { target } }} 60 | cargo test test_metrics {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" {"dev"} else { target } }} -- --ignored 61 | 62 | test-inprocess target=default-target: 63 | {{ if target == "debug" { "cargo test --features='inprocess'; cargo test test_metrics --features='inprocess' -- --ignored; cargo test test_gather_metrics --features='inprocess' -- --ignored" } else {"echo 'inprocess tests are not run for release builds'" } }} 64 | 65 | test-seccomp target=default-target features="": 66 | cargo test {{ if features =="" {'--no-default-features -F "kvm,mshv2,seccomp"'} else {"--no-default-features -F seccomp," + features } }} --profile={{ if target == "debug" {"dev"} else { target } }} -- --test-threads=1 67 | cargo test {{ if features =="" {'--no-default-features -F "kvm,mshv2,seccomp"'} else {"--no-default-features -F seccomp," + features } }} test_metrics --profile={{ if target == "debug" {"dev"} else { target } }} -- --ignored --test-threads=1 68 | cargo test {{ if features =="" {'--no-default-features -F "kvm,mshv2,seccomp"'} else {"--no-default-features -F seccomp," + features } }} test_gather_metrics --profile={{ if target == "debug" {"dev"} else { target } }} -- --ignored --test-threads=1 69 | 70 | examples-ci target=default-target features="": (build-rust-wasm-examples target) 71 | cargo run {{ if features =="" {''} else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" {"dev"} else { target } }} --example helloworld 72 | cargo run {{ if features =="" {''} else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" {"dev"} else { target } }} --example hostfuncs 73 | cargo run {{ if features =="" {''} else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" {"dev"} else { target } }} --example rust_wasm_examples 74 | cargo run {{ if features =="" {''} else {"--no-default-features -F function_call_metrics," + features } }} --profile={{ if target == "debug" {"dev"} else { target } }} --example metrics 75 | cargo run {{ if features =="" {"--no-default-features --features kvm,mshv2"} else {"--no-default-features -F function_call_metrics," + features } }} --profile={{ if target == "debug" {"dev"} else { target } }} --example metrics 76 | 77 | # warning, compares to and then OVERWRITES the given baseline 78 | bench-ci baseline target=default-target features="": 79 | cd src/hyperlight_wasm && cargo bench --profile={{ if target == "debug" {"dev"} else { target } }} {{ if features =="" {''} else { "--features " + features } }} -- --verbose --save-baseline {{baseline}} 80 | bench target=default-target features="": 81 | cd src/hyperlight_wasm && cargo bench --profile={{ if target == "debug" {"dev"} else { target } }} {{ if features =="" {''} else { "--features " + features } }} -- --verbose 82 | bench-download os hypervisor tag="": 83 | gh release download {{ tag }} -D ./src/hyperlight_wasm/target/ -p benchmarks_{{ os }}_{{ hypervisor }}.tar.gz 84 | mkdir {{ mkdir-arg }} ./src/hyperlight_wasm/target/criterion 85 | tar -zxvf ./src/hyperlight_wasm/target/benchmarks_{{ os }}_{{ hypervisor }}.tar.gz -C ./src/hyperlight_wasm/target/criterion/ --strip-components=1 86 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright (c) Microsoft Corporation. All Rights Reserved. 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hyperlight-Wasm - Run Wasm Modules in a Virtual Machine backed sandbox 2 | 3 | _Hyperlight-Wasm_ is a component that enables Wasm Modules to be run inside lightweight Virtual Machine backed Sandbox. Its purpose is to enable applications to safely run untrusted or third party Wasm code within a VM with very low latency/overhead. It is built on top of [Hyperlight](https://github.com/hyperlight-dev/hyperlight). 4 | 5 | Hyperlight-Wasm currently supports running applications using either the [Windows Hypervisor Platform](https://docs.microsoft.com/en-us/virtualization/api/#windows-hypervisor-platform) on Windows, [KVM](https://www.linux-kvm.org/page/Main_Page) on Linux or [/dev/mshv](https://github.com/rust-vmm/mshv). 6 | 7 | WARNING: This is experimental code. It is not considered production-grade by its developers, neither is it "supported" software. 8 | 9 | This repo contains hyperlight-wasm along with a couple of sample Wasm modules and an example host application that can be used to either test or try it out. 10 | 11 | This document contains instructions on building `Hyperlight-Wasm`. For usage instructions, please see [RustDev.md](./RustDev.md). 12 | 13 | ## Prerequisites 14 | 15 | ### Windows 16 | 17 | Make sure the following are installed: 18 | 19 | 1. [Rust](https://www.rust-lang.org/tools/install) 20 | 1. [just](https://github.com/casey/just). is used as a command runner `cargo install just`. Do not install `just` with Chocolately because it installs an older incompatible version, make sure to use at least version 1.5.0 if not installed through cargo. 21 | 1. [pwsh](https://github.com/PowerShell/PowerShell) 22 | 1. [git](https://gitforwindows.org/) 23 | 1. [GitHub CLI](https://github.com/cli/cli#installation) 24 | 25 | Ensure that Windows Hypervisor Platform is enabled: 26 | 27 | ```PowerShell 28 | Enable-WindowsOptionalFeature -Online -FeatureName HyperVisorPlatform 29 | ``` 30 | 31 | ### Linux 32 | 33 | #### Ubuntu/KVM 34 | 35 | Make sure the following are installed: 36 | 37 | 1. build-essential: `sudo apt install build-essential` 38 | 1. [Rust](https://www.rust-lang.org/tools/install) `curl --proto '=https' --tlsv1.3 https://sh.rustup.rs -sSf | sh` 39 | 1. [just](https://github.com/casey/just). is used as a command runner `cargo install just`. Do not install `just` through a package manager as it may install an older incompatible version, make sure to use at least version 1.5.0 if not installed through cargo. 40 | 1. [GitHub CLI](https://github.com/cli/cli#installation) 41 | 42 | Ensure that KVM is enabled: On an Azure VM using a size that supports nested virtualisation: 43 | 44 | `sudo apt install cpu-checker && kvm-ok` 45 | 46 | If KVM is installed and available you should see the output 47 | 48 | ``` console 49 | INFO: /dev/kvm exists 50 | KVM acceleration can be used 51 | ``` 52 | 53 | You should also add your user to the kvm group: `sudo adduser $USER kvm` 54 | 55 | ## Building 56 | 57 | NOTE: Ensure that you use version 1.82.0 of rust toolchain. 58 | 59 | ```Console 60 | rustup install 1.82.0 61 | rustup default 1.82.0 62 | ``` 63 | 64 | Now you can build the Rust Wasm library: 65 | 66 | ```Console 67 | just build 68 | ``` 69 | 70 | And the example wasm modules used by the tests: 71 | ```Console 72 | just build-wasm-examples 73 | just build-rust-wasm-examples 74 | ``` 75 | 76 | Then you can test the Rust Wasm library and run the example: 77 | 78 | ```Console 79 | just test 80 | cargo run --example helloworld 81 | ``` 82 | 83 | ## Component Model support 84 | 85 | Hyperlight-Wasm has experimental support for running WebAssembly 86 | Component Model components, rather than core wasm modules. In this 87 | mode, set the `HYPERLIGHT_WASM_WORLD` environment variable to point to 88 | a binary encoding of a component type (e.g. the result of running 89 | `wasm-tools component wit -w -o /path/to/output.wasm 90 | /path/to/input.wit`), which will ensure that the resultant library 91 | includes a guest binary that is specialised to load components with 92 | that type. Then, use `hyperlight_component_macro::host_bindgen!()` to 93 | generate bindings from the same component type in the host. For a 94 | complete (albeit small) example of this, see [this 95 | example](https://aka.ms/hyperlight-wasm-sockets-sample). 96 | 97 | > [!NOTE] 98 | > Currently, component model support in Hyperlight-Wasm requires using 99 | > [the `hyperlight-component-macro` branch of core 100 | > hyperlight](https://github.com/hyperlight-dev/hyperlight/pull/376). The 101 | > Hyperlight-Wasm [`Cargo.toml`](./Cargo.toml) already depends on this 102 | > version, rather than the published one, but you should be careful to 103 | > use the same dependency specification to avoid Cargo pulling two 104 | > instances of core hyperlight into your dependency graph. 105 | 106 | ## Code of Conduct 107 | 108 | This project has adopted the [Microsoft Open Source Code of 109 | Conduct](https://opensource.microsoft.com/codeofconduct/). 110 | 111 | For more information see the [Code of Conduct 112 | FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact 113 | [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 114 | 115 | ## FOSSA Status 116 | [![FOSSA License Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fhyperlight-dev%2Fhyperlight-wasm.svg?type=shield&issueType=license)](https://app.fossa.com/projects/git%2Bgithub.com%2Fhyperlight-dev%2Fhyperlight-wasm?ref=badge_shield&issueType=license) 117 | [![FOSSA Security Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fhyperlight-dev%2Fhyperlight-wasm.svg?type=shield&issueType=security)](https://app.fossa.com/projects/git%2Bgithub.com%2Fhyperlight-dev%2Fhyperlight-wasm?ref=badge_shield&issueType=security) 118 | -------------------------------------------------------------------------------- /RustDev.md: -------------------------------------------------------------------------------- 1 | # Using the Rust Hyperlight-Wasm SDK 2 | 3 | While the [README](./README.md) document details how to build the hyperlight-wasm SDK for use, this document details how to use in a host application. 4 | 5 | These APIs are still unstable, and may change significantly in the 6 | future as we explore alternative ways to construct/cache/etc 7 | sandboxes. 8 | 9 | ## WebAssembly Component vs WebAssembly Module APIs 10 | 11 | Hyperlight-Wasm currently supports running both core WebAssembly 12 | modules and WebAssembly Component Model components. While the core 13 | structures discussed below are the same in either mode, the details of 14 | registering host functions and calling guest functions are quite 15 | different. 16 | 17 | To use a WebAssembly component in hyperlight-wasm, the binary-encoded 18 | component type of the relevant encoding (of the form produced by 19 | encoding a WIT package; in particular, instance imports/exports need 20 | WIT-formatted "wit:package/instance@version" names) needs to be 21 | available at compile-time. To enable this world, set 22 | `HYPERLIGHT_WASM_WORLD` when building `hyperlight-wasm`: 23 | 24 | ```sh 25 | wasm-tools component wit -w -o world.wasm world.wit 26 | export HYPERLIGHT_WASM_WORLD=$(readlink -f world.wasm) 27 | ``` 28 | 29 | ## `ProtoWasmSandbox` vs `WasmSandbox` vs `LoadedWasmSandbox` 30 | 31 | The three primary APIs with which you'll be interacting are [`ProtoWasmSandbox`](./src/hyperlight_wasm/src/proto_wasm_sandbox.rs), [`WasmSandbox`](./src/hyperlight_wasm/src/wasm_sandbox.rs) and [`LoadedWasmSandbox`](./src/hyperlight_wasm//s 32 | rc/loaded_wasm_sandbox.rs). These are three different Rust `struct`s that provide a type-safe way to ensure the current state of the system. 33 | 34 | One helpful way to think about these three different types is as a strongly-typed state machine that is enforced by the Rust compiler. This type-safety feature is important because it allows you, the application developer, to detect issues sooner, and without having to write as many tests (because the compiler is doing more checks for you). 35 | 36 | We'll be detailing Hyperlight-Wasm in this document using this state machine concept. 37 | 38 | ### More about `ProtoWasmSandbox` 39 | 40 | The `ProtoWasmSandbox` is the initial type to be created using a `SandboxBuilder`. This type allows for registration of host functions (these are functions that the host application wants to make available to be called by code running inside the sandbox). The `ProtoWasmSandbox` type is a sandbox that has been initialized with host functions, 41 | but has no Wasm runtime loaded. Once it has been transitioned to a `WasmSandbox` type, host functions can no longer be registered. 42 | 43 | ### More about `WasmSandbox` 44 | 45 | The `WasmSandbox` represents a sandbox state that is not completely ready for use. While it does have the `wasm_runtime` guest binary loaded into it (see [Rust.md](./Rust.md) for more details on this guest binary), and the Wasm runtime initialised, it is missing a user's WebAssembly module code. The 'WasmSandbox' type is an intermediate state that is designed to be cached in a host to avoid having to pay the cost of loading the `wasm_runtime` guest binary and initializing the Wasm runtime each time a new user code module is loaded. 46 | 47 | Loading user code is the final initialization step necessary to have a ready-to-use sandbox, so moving from the `WasmSandbox` state to the `LoadedWasmSandbox` state requires specifying what user code to load. See the "State transitions" section below for details on making this state transition. 48 | 49 | ### More about `LoadedWasmSandbox` 50 | 51 | In the previous section, we mentioned that `WasmSandbox` is an intermediate state and `LoadedWasmSandbox` is a "final" state. In other words, if you have a `LoadedWasmSandbox` type, you are ready to execute customer-provided workloads. Do so by calling the `call_guest_function` function when using a WebAssembly module, or by using an autogenerated wrapper when using a WebAssembly component. In addition, the `LoadedWasmSandbox` type can be safely transitioned back to a `WasmSandbox` type. This is useful if you want cache the `WasmSandbox` prior to loading a different user code module. 52 | 53 | ## Making transitions between `ProtoWasmSandbox` `WasmSandbox` and `LoadedWasmSandbox` 54 | 55 | This library includes a standardized way to make transitions between `ProtoWasmSandbox` , `WasmSandbox` and `LoadedWasmSandbox`. 56 | 57 | To transition from `ProtoWasmSandbox` to `WasmSandbox`, use the `load_runtime()` method. This method consumes the `ProtoWasmSandbox` and returns a `WasmSandbox`. This method is useful when you want to cache a `WasmSandbox` for later use, but you don't yet have a user code module to load. 58 | 59 | To transition from `WasmSandbox` to `LoadedWasmSandbox`, use the `load_module()` method. This method consumes the `WasmSandbox` and returns a `LoadedWasmSandbox`. 60 | 61 | To transition from `LoadedWasmSandbox` to `WasmSandbox`, use the `unload_module()` method. This method consumes the `LoadedWasmSandbox` and returns a `WasmSandbox`. This method is useful when you want to reuse a `WasmSandbox` for a different code module. 62 | 63 | This entire process is type safe, and thus we enforce valid state transition orders in the type system. What this all means for you is the following: 64 | 65 | >The Rust compiler checks your code for the right "state" transitions. In other words, it's impossible for you to compile code that does the wrong thing. 66 | 67 | Please see our [Hello World example](./src/hyperlight_wasm/examples/helloworld/main.rs) for a complete, compilable code illustrating how to do these transitions through the state machine. 68 | 69 | ## Registering host functions 70 | 71 | Before you can transition a `ProtoWasmSandbox` to a `WasmSandbox` (and on to a `LoadedWasmSandbox` where you can call guest functions), you need to register the "host functions" that the guest can use: the guest can call back into these functions while it is executing. 72 | 73 | ### Registering host functions in module mode 74 | 75 | In module mode, register host functions with `ProtoWasmSandbox::register_host_func_i`. In this method, the `i` suffix indicates the number of parameters your host function has. Currently a number in the range from `0` to `3` (inclusive) is supported. 76 | 77 | Please see an illustration of how to do this in the [hostfunc/main.rs](./src/hyperlight_wasm/examples/hostfunc/main.rs) Rust example. 78 | 79 | #### Parameter and return types 80 | 81 | Hyperlight host functions support a limited set of types for parameters and return values. Below is a list of types supported by each: 82 | 83 | - `()` (nothing): ❌ parameter type, ✅ return type 84 | - `String`: ✅ parameter type, ✅ return type 85 | - `i32`: ✅ parameter type, ✅ return type 86 | - `i64`: ✅ parameter type, ✅ return type 87 | - `bool`: ✅ parameter type, ✅ return type 88 | - `Vec`: ✅ parameter type, ✅ return type 89 | 90 | ### Registering host functions in component mode 91 | 92 | If you are using the component mode, register host functions by 93 | generating host bindings with 94 | `hyperlight_component_macro::host_bindgen!();` and then calling the 95 | created `register_host_functions`, which will also close over an 96 | arbitrary state structure of your design: 97 | 98 | ```rust 99 | impl wit::package::Component for State { 100 | // ... 101 | } 102 | let rt = register_host_functions(&mut proto_wasm_sandbox, state); 103 | ``` 104 | 105 | This will return the "resource table" that is, as described above, 106 | required when calling host functions. 107 | 108 | 109 | ## Calling guest functions 110 | 111 | Assuming you have a `LoadedWasmSandbox` (using the `WasmSandbox::load_module` method as described in the previous section), you can call functions exported by the WebAssembly module or component that you've loaded. 112 | 113 | ### Calling WebAssembly module exports with `call_guest_function` 114 | 115 | You can execute module exports use `call_guest_function`, as shown in 116 | this example: 117 | 118 | ```rust 119 | use hyperlight_wasm::{ParameterValue, ReturnType, ReturnValue}; 120 | 121 | // Assumptions: 122 | // 123 | // - loaded_sbox is a LoadedWasmSandbox type 124 | // - it has a Wasm module loaded that exports a 'max' function that accepts 125 | // two int32 parameters and returns one containing the value of the maximum 126 | // of the two given parameters 127 | let guest_func_ret: ReturnValue = loaded_sbox.call_guest_function( 128 | "my_function", 129 | Some(vec![ParameterValue::Int(1), ParameterValue::Int(2)]), 130 | ReturnType::Int, 131 | )? 132 | // at this point, we expect guest_func_ret to be a ReturnValue::Int, so 133 | // let's check for that here 134 | match guest_func_ret { 135 | ReturnValue::Int(i_val) => if i_val != 2 { 136 | panic!("got {i_val} from the guest, but expected 2"); 137 | }, 138 | other => panic!("we didn't get the expected type back from our guest call"), 139 | }; 140 | ``` 141 | 142 | In the above code snippet, we call a guest function named `my_function` that takes two `i32` parameters and returns an `i32`. We expect the return value to be `2`, and we panic if it is not. Once the guest function is complete the state of the `LoadedWasmSandbox` is reverted to the state as it was before the call. 143 | 144 | There are more detailed full examples of calling module exports in 145 | these examples: 146 | - [helloworld/main.rs](./src/hypuncerlight_wasm/examples/helloworld/main.rs) 147 | - [hostfunc/main.rs](./src/hyperlight_wasm/examples/hostfunc/main.rs) 148 | 149 | ### Calling WebAssembly component exports with generated bindings 150 | 151 | Assuming that you have registered the component imports as a host 152 | function, as described above, and gotten a `LoadedWasmSandbox`, you 153 | will need to construct a `wit::package::ComponentSandbox` structure 154 | that contains the sandbox itself along with the `rts` resource table that you 155 | received when registering host functions: 156 | 157 | ``` 158 | let mut wrapped = wit::package::ComponentSandbox { 159 | sb: loaded_wasm_sandbox, 160 | rt: rt, 161 | }; 162 | ``` 163 | 164 | The resultant structure will implement a generated trait called 165 | `wit::package::ComponentExports` that allows you to extract exported 166 | instances/functions and call them. Currently, running `cargo doc 167 | --document-private-items` is the easiest way to see the trait bindings 168 | produced for a particular world. 169 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | ## Reporting Security Issues 2 | 3 | **Please do not report security vulnerabilities through public GitHub issues.** 4 | 5 | Instead, please report them via the [GitHub's private security vulnerability reporting mechanism on this repo](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/privately-reporting-a-security-vulnerability#privately-reporting-a-security-vulnerability). 6 | 7 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 8 | 9 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 10 | * Full paths of source file(s) related to the manifestation of the issue 11 | * The location of the affected source code (tag/branch/commit or direct URL) 12 | * Any special configuration required to reproduce the issue 13 | * Step-by-step instructions to reproduce the issue 14 | * Proof-of-concept or exploit code (if possible) 15 | * Impact of the issue, including how an attacker might exploit the issue 16 | 17 | This information will help us triage your report more quickly. 18 | 19 | ## Preferred Languages 20 | 21 | We prefer all communications to be in English. 22 | -------------------------------------------------------------------------------- /SUPPORT.md: -------------------------------------------------------------------------------- 1 | # Support 2 | 3 | ## How to file issues and get help 4 | 5 | This project uses GitHub Issues to track bugs and feature requests. Please search the existing 6 | issues before filing new issues to avoid duplicates. For new issues, file your bug or 7 | feature request as a new Issue. 8 | 9 | [//]: <> (For help and questions about using this project, please use Slack channel [TODO: add Slack channel]) 10 | 11 | ## Microsoft Support Policy 12 | 13 | Support for **hyperlight-wasm** is limited to the resources listed above. 14 | -------------------------------------------------------------------------------- /docs/github-labels.md: -------------------------------------------------------------------------------- 1 | # **Pull Requests (PRs)** 2 | 3 | We use GitHub labels to categorize PRs. Before a PR can be merged, it must be assigned one of the following **kind/** labels: 4 | 5 | - **kind/bugfix** - For PRs that fix bugs. 6 | - **kind/dependencies** - For PRs that update dependencies or related components. 7 | - **kind/enhancement** - For PRs that introduce new features or improve existing functionality. This label also applies to improvements in documentation, testing, and similar areas. Any changes must be backward-compatible. 8 | - **kind/refactor** - For PRs that restructure or remove code without adding new functionality. This label also applies to changes that affect user-facing APIs. 9 | 10 | --- 11 | 12 | # **Issues** 13 | 14 | Issues are categorized using the following three **GitHub types** (not GitHub labels): 15 | 16 | - **bug** - Reports an unexpected problem or incorrect behavior. 17 | - **design** - Relates to design considerations or decisions. 18 | - **enhancement** - Suggests a new feature, improvement, or idea. 19 | 20 | To track the lifecycle of issues, we also use GitHub labels: 21 | 22 | - **lifecycle/needs-review** - A temporary label indicating that the issue has not yet been reviewed. 23 | - **lifecycle/confirmed** - Confirms the issue’s validity: 24 | - If the issue type is **bug**, the bug has been verified. 25 | - If the issue type is **enhancement**, the proposal is considered reasonable but does not guarantee implementation. 26 | - This label does not indicate when or if the fix or enhancement will be implemented. 27 | - **lifecycle/needs-info** - The issue requires additional information from the original poster (OP). 28 | - **lifecycle/blocked** - The issue is blocked by another issue or external factor. 29 | 30 | The following labels should be applied to issues prior to closing, indicating the resolution status of the issue: 31 | 32 | - **lifecycle/duplicate** - The issue is a duplicate of another issue. 33 | - **lifecycle/fixed** - The issue has been resolved. 34 | - **lifecycle/not-a-bug** - The issue is not considered a bug, and no further action is needed. 35 | - **lifecycle/wont-fix** - The issue will not be fixed. 36 | 37 | In addition to lifecycle labels, we use the following labels to further categorize issues: 38 | 39 | - **good-first-issue** - The issue is suitable for new contributors or those looking for a simple task to start with. 40 | - **help-wanted** - The issue is a request for help or assistance. 41 | - **question** - The issue is a question or request for information. 42 | 43 | --- 44 | 45 | # **Issues & PRs** 46 | 47 | In addition to **kind/*** labels, we use optional **area/*** labels to specify the focus of a PR or issue. These labels are purely for categorization, and are not mandatory. 48 | 49 | - **area/API** - Related to the API or public interface. 50 | - **area/dependencies** - Concerns dependencies or related components. This label is different from **kind/dependencies**, which should only used for PRs. 51 | - **area/documentation** - Related to documentation updates or improvements. 52 | - **area/infrastructure** - Concerns infrastructure rather than core functionality. 53 | - **area/performance** - Addresses performance. 54 | - **area/security** - Involves security-related changes or fixes. 55 | - **area/testing** - Related to tests or testing infrastructure. 56 | 57 | 58 | ## Notes 59 | This document is a work in progress and may be updated as needed. The labels and categories are subject to change based on the evolving needs of the project and community feedback. 60 | -------------------------------------------------------------------------------- /docs/observability.md: -------------------------------------------------------------------------------- 1 | # Observability 2 | 3 | hyperlight-wasm provides the following observability features: 4 | 5 | * [Metrics](#metrics) metrics are provided using `metrics` crate. 6 | 7 | ## Metrics 8 | 9 | 10 | The following metrics are provided and are enabled by default: 11 | 12 | * `active_proto_wasm_sandboxes` - A gauge indicating the number of currently active proto wasm sandboxes 13 | * `active_wasm_sandboxes` - A gauge indicating the number of currently active wasm sandboxes 14 | * `active_loaded_wasm_sandboxes` - A gauge indicating the number of currently loaded wasm sandboxes 15 | * `proto_wasm_sandboxes_total` - A counter indicating the total number of proto wasm sandboxes created during the lifetime of the process 16 | * `wasm_sandboxes_total` - A counter indicating the total number of wasm sandboxes created during the lifetime of the process 17 | * `loaded_wasm_sandboxes_total` - A counter indicating the total number of loaded wasm sandboxes created during the lifetime of the process 18 | * `sandbox_loads_total` - A counter indicating how many times a wasm sandbox has been loaded into a loaded wasm sandbox during the lifetime of the process 19 | * `sandbox_unloads_total` - A counter indicating how many times a loaded wasm sandbox has been unloaded into a wasm sandbox during the lifetime of the process 20 | 21 | 22 | In addition, regular Hyperlight provides the following metrics: 23 | 24 | * `guest_errors_total` - A counter indicating how many times a guest error has occurred 25 | * `guest_cancellations_total` - The number of times guest execution has timed out 26 | 27 | If cargo feature `function_call_metrics` is enabled: 28 | 29 | * `guest_call_duration_seconds` - Histogram for the execution time of guest function calls 30 | * `host_call_duration_seconds` - Histogram for the execution time of host function calls 31 | 32 | There is an example of how to gather metrics in the [examples/metrics](../src/hyperlight_wasm/examples/metrics) directory. 33 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "1.82.0" 3 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | newline_style = "Native" 2 | unstable_features = true # Cargo fmt now needs to be called with `cargo +nightly fmt` 3 | group_imports = "StdExternalCrate" # create three groups for std, external and local crates 4 | # Merge imports from the same module 5 | # See: https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#imports_granularity 6 | imports_granularity = "Module" -------------------------------------------------------------------------------- /src/examples_common/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "examples_common" 3 | version = "0.9.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | [dependencies] 8 | hyperlight-host = { workspace = true } 9 | 10 | [lib] 11 | bench = false # see https://bheisler.github.io/criterion.rs/book/faq.html#cargo-bench-gives-unrecognized-option-errors-for-valid-command-line-options 12 | -------------------------------------------------------------------------------- /src/examples_common/src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 The Hyperlight Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | use std::env; 18 | use std::path::Path; 19 | 20 | use hyperlight_host::{new_error, Result}; 21 | 22 | /// Get the Wasm module called `name` from the standard module location 23 | /// at `$REPO_ROOT/x64/[debug|release]/$name`. 24 | pub fn get_wasm_module_path(name: &str) -> Result { 25 | #[cfg(debug_assertions)] 26 | let config = "debug"; 27 | #[cfg(not(debug_assertions))] 28 | let config = "release"; 29 | 30 | let proj_dir = env::var_os("CARGO_MANIFEST_DIR").unwrap_or_else(|| { 31 | env::var_os("RUST_DIR_FOR_DEBUGGING_TESTS") 32 | .expect("Failed to get CARGO_MANIFEST_DIR or RUST_DIR_FOR_DEBUGGING_TESTS env var") 33 | }); 34 | 35 | let x64_dir = Path::new(&proj_dir) 36 | .join("../../x64") 37 | .join(config) 38 | .join(name); 39 | 40 | let path = match x64_dir.canonicalize() { 41 | Ok(path) => path.to_str().unwrap().to_string(), 42 | Err(e) => { 43 | return Err(new_error!( 44 | "Failed to get the path to the Wasm module: {:?} {}", 45 | x64_dir, 46 | e, 47 | )); 48 | } 49 | }; 50 | Ok(path) 51 | } 52 | -------------------------------------------------------------------------------- /src/hyperlight_wasm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hyperlight-wasm" 3 | version = "0.1.0" 4 | edition = "2021" 5 | rust-version = "1.82" 6 | include = ["*"] # Make sure wasm_runtime is included! 7 | 8 | [lib] 9 | name = "hyperlight_wasm" 10 | bench = false # see https://bheisler.github.io/criterion.rs/book/faq.html#cargo-bench-gives-unrecognized-option-errors-for-valid-command-line-options 11 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 12 | 13 | [[example]] 14 | name = "helloworld" 15 | path = "examples/helloworld/main.rs" 16 | test = true 17 | 18 | [[example]] 19 | name = "hostfuncs" 20 | path = "examples/hostfuncs/main.rs" 21 | test = true 22 | 23 | [[example]] 24 | name = "rust_wasm_examples" 25 | path = "examples/rust_wasm_examples/main.rs" 26 | test = true 27 | 28 | [[example]] 29 | name = "metrics" 30 | path = "examples/metrics/main.rs" 31 | test = true 32 | 33 | [dependencies] 34 | hyperlight-host = { workspace = true } 35 | libc = { version = "0.2.172" } 36 | once_cell = "1.21.3" 37 | tracing = "0.1.27" 38 | log = "0.4.27" 39 | cfg-if = { version = "1" } 40 | metrics = "0.24.2" 41 | 42 | [target.'cfg(windows)'.dependencies] 43 | windows = { version = "0.61", features = ["Win32_System_Threading"] } 44 | page_size = "0.6.0" 45 | 46 | [dev-dependencies] 47 | examples_common = { path = "../examples_common" } 48 | criterion = { version = "0.6.0", features = ["html_reports"] } 49 | crossbeam-queue = "0.3" 50 | blake3 = "1.8" 51 | toml = "0.8.22" 52 | metrics-util = "0.19.1" 53 | metrics-exporter-prometheus = "0.17" 54 | 55 | [build-dependencies] 56 | chrono = "0.4" 57 | blake3 = "1.8" 58 | built = { version = "0.8.0", features = ["chrono", "git2"] } 59 | anyhow = { version = "1.0" } 60 | goblin = "0.9.3" 61 | 62 | [features] 63 | default = ["function_call_metrics", "kvm", "mshv2"] 64 | function_call_metrics = ["hyperlight-host/function_call_metrics"] 65 | seccomp = ["hyperlight-host/seccomp"] 66 | print_debug = ["hyperlight-host/print_debug"] 67 | crashdump = ["hyperlight-host/crashdump"] 68 | kvm = ["hyperlight-host/kvm"] 69 | mshv2 = ["hyperlight-host/mshv2"] 70 | mshv3 = ["hyperlight-host/mshv3"] 71 | inprocess = ["hyperlight-host/inprocess"] 72 | 73 | [[bench]] 74 | name = "benchmarks" 75 | harness = false 76 | -------------------------------------------------------------------------------- /src/hyperlight_wasm/benches/benchmarks.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 The Hyperlight Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | use std::sync::{Arc, Mutex}; 18 | 19 | use criterion::{criterion_group, criterion_main, Bencher, Criterion}; 20 | use hyperlight_host::HyperlightError; 21 | use hyperlight_wasm::{LoadedWasmSandbox, ParameterValue, Result, ReturnType, SandboxBuilder}; 22 | 23 | fn get_time_since_boot_microsecond() -> Result { 24 | let res = std::time::SystemTime::now() 25 | .duration_since(std::time::SystemTime::UNIX_EPOCH)? 26 | .as_micros(); 27 | i64::try_from(res).map_err(HyperlightError::IntConversionFailure) 28 | } 29 | 30 | fn wasm_guest_call_benchmark(c: &mut Criterion) { 31 | let mut group = c.benchmark_group("wasm_guest_functions"); 32 | 33 | let bench_guest_function = |b: &mut Bencher<'_>, ext| { 34 | let mut loaded_wasm_sandbox = get_loaded_wasm_sandbox(ext); 35 | 36 | b.iter(|| { 37 | loaded_wasm_sandbox 38 | .call_guest_function( 39 | "Echo", 40 | Some(vec![ParameterValue::String("Hello World!".to_string())]), 41 | ReturnType::String, 42 | ) 43 | .unwrap() 44 | }); 45 | }; 46 | 47 | group.bench_function("wasm_guest_call", |b: &mut Bencher<'_>| { 48 | bench_guest_function(b, "wasm"); 49 | }); 50 | 51 | group.bench_function("wasm_guest_call_aot", |b: &mut Bencher<'_>| { 52 | bench_guest_function(b, "aot"); 53 | }); 54 | 55 | group.finish(); 56 | } 57 | 58 | fn wasm_sandbox_benchmark(c: &mut Criterion) { 59 | let mut group = c.benchmark_group("wasm_sandboxes"); 60 | let create_wasm_sandbox = || { 61 | get_loaded_wasm_sandbox("wasm"); 62 | }; 63 | 64 | group.bench_function("create_sandbox", |b| { 65 | b.iter_with_large_drop(create_wasm_sandbox); 66 | }); 67 | 68 | group.bench_function("create_sandbox_and_drop", |b| { 69 | b.iter(create_wasm_sandbox); 70 | }); 71 | 72 | group.finish(); 73 | } 74 | 75 | fn get_loaded_wasm_sandbox(ext: &str) -> LoadedWasmSandbox { 76 | let mut sandbox = SandboxBuilder::new().build().unwrap(); 77 | 78 | let get_time_since_boot_microsecond_func = 79 | Arc::new(Mutex::new(get_time_since_boot_microsecond)); 80 | 81 | sandbox 82 | .register_host_func_0( 83 | "GetTimeSinceBootMicrosecond", 84 | &get_time_since_boot_microsecond_func, 85 | ) 86 | .unwrap(); 87 | 88 | let wasm_sandbox = sandbox.load_runtime().unwrap(); 89 | 90 | wasm_sandbox 91 | .load_module(format!("../../x64/release/RunWasm.{ext}",)) 92 | .unwrap() 93 | } 94 | 95 | criterion_group! { 96 | name = benches; 97 | config = Criterion::default();//.warm_up_time(Duration::from_millis(50)); // If warm_up_time is default 3s warmup, the benchmark will fail due memory error 98 | targets = wasm_guest_call_benchmark, wasm_sandbox_benchmark 99 | } 100 | criterion_main!(benches); 101 | -------------------------------------------------------------------------------- /src/hyperlight_wasm/build.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 The Hyperlight Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // build.rs 18 | 19 | // The purpose of this build script is to embed the wasm_runtime binary as a resource in the hyperlight-wasm binary. 20 | // This is done by reading the wasm_runtime binary into a static byte array named WASM_RUNTIME. 21 | // this build script writes the code to do that to a file named wasm_runtime_resource.rs in the OUT_DIR. 22 | // this file is included in lib.rs. 23 | // The wasm_runtime binary is expected to be in the x64/{config} directory. 24 | 25 | use std::fs::OpenOptions; 26 | use std::io::Write; 27 | use std::path::{Path, PathBuf}; 28 | use std::{env, fs}; 29 | 30 | use anyhow::Result; 31 | use built::write_built_file; 32 | 33 | fn build_wasm_runtime() -> PathBuf { 34 | let proj_dir = env::var_os("CARGO_MANIFEST_DIR").unwrap(); 35 | let profile = env::var_os("PROFILE").unwrap(); 36 | 37 | let in_repo_dir = PathBuf::from(&proj_dir) 38 | .parent() 39 | .unwrap_or_else(|| panic!("could not find parent of cargo manifest directory")) 40 | .join("wasm_runtime"); 41 | if !in_repo_dir.exists() { 42 | panic!("hyperlight_wasm does not yet support being compiled from a release package"); 43 | } 44 | print!("cargo::rerun-if-changed="); 45 | let _ = std::io::stdout() 46 | .write_all(AsRef::::as_ref(&in_repo_dir).as_encoded_bytes()); 47 | println!(); 48 | println!("cargo::rerun-if-env-changed=HYPERLIGHT_WASM_WORLD"); 49 | // the PROFILE env var unfortunately only gives us 1 bit of "dev or release" 50 | let cargo_profile = if profile == "debug" { "dev" } else { "release" }; 51 | let mut cmd = std::process::Command::new("cargo"); 52 | 53 | // Clear the variables that control Cargo's behaviour (as listed 54 | // at https://doc.rust-lang.org/cargo/reference/environment-variables.html): 55 | // otherwise the nested build will build the wrong thing 56 | let mut env_vars = env::vars().collect::>(); 57 | env_vars.retain(|(key, _)| !key.starts_with("CARGO_")); 58 | 59 | let cmd = cmd 60 | .args(["build", &format!("--profile={}", cargo_profile), "-v"]) 61 | .current_dir(&in_repo_dir) 62 | .env_clear() 63 | .envs(env_vars); 64 | 65 | let status = cmd 66 | .status() 67 | .unwrap_or_else(|e| panic!("could not run cargo build: {}", e)); 68 | if !status.success() { 69 | panic!("could not compile wasm_runtime"); 70 | } 71 | let resource = in_repo_dir 72 | .join("target") 73 | .join("x86_64-unknown-none") 74 | .join(profile) 75 | .join("wasm_runtime"); 76 | 77 | resource.canonicalize().unwrap_or_else(|_| { 78 | panic!( 79 | "could not find wasm_runtime after building it (expected {:?})", 80 | resource 81 | ) 82 | }) 83 | } 84 | 85 | fn main() -> Result<()> { 86 | let wasm_runtime_resource = build_wasm_runtime(); 87 | 88 | let out_dir = env::var_os("OUT_DIR").unwrap(); 89 | let dest_path = Path::new(&out_dir).join("wasm_runtime_resource.rs"); 90 | let contents = format!( 91 | "pub (super) static WASM_RUNTIME: [u8; include_bytes!({name:?}).len()] = *include_bytes!({name:?});", 92 | name = wasm_runtime_resource.as_os_str() 93 | ); 94 | 95 | fs::write(dest_path, contents).unwrap(); 96 | 97 | // get the wasmtime version number from the wasm_runtime metadata 98 | 99 | let wasm_runtime_bytes = fs::read(&wasm_runtime_resource).unwrap(); 100 | let elf = goblin::elf::Elf::parse(&wasm_runtime_bytes).unwrap(); 101 | 102 | // the wasm_runtime binary has a section named .note_hyperlight_metadata that contains the wasmtime version number 103 | // this section is added to the wasm_runtime binary by the build.rs script in the wasm_runtime crate 104 | let section_name = ".note_hyperlight_metadata"; 105 | let wasmtime_version_number = if let Some(header) = elf.section_headers.iter().find(|hdr| { 106 | if let Some(name) = elf.shdr_strtab.get_at(hdr.sh_name) { 107 | name == section_name 108 | } else { 109 | false 110 | } 111 | }) { 112 | let start = header.sh_offset as usize; 113 | let size = header.sh_size as usize; 114 | let end = start + size; 115 | let metadata_bytes = &wasm_runtime_bytes[start..end]; 116 | // convert the metadata bytes to a string 117 | if let Some(null_pos) = metadata_bytes.iter().position(|&b| b == 0) { 118 | std::str::from_utf8(&metadata_bytes[..null_pos]).unwrap() 119 | } else { 120 | std::str::from_utf8(metadata_bytes).unwrap() 121 | } 122 | } else { 123 | panic!(".note_hyperlight_metadata section not found in wasm_runtime binary"); 124 | }; 125 | 126 | // write the build information to the built.rs file 127 | write_built_file()?; 128 | 129 | // open the built.rs file and append the details of the wasm_runtime file 130 | let built_path = Path::new(&out_dir).join("built.rs"); 131 | let mut file = OpenOptions::new() 132 | .create(false) 133 | .append(true) 134 | .open(built_path) 135 | .unwrap(); 136 | 137 | let metadata = fs::metadata(&wasm_runtime_resource).unwrap(); 138 | let created = metadata.modified().unwrap(); 139 | let created_datetime: chrono::DateTime = created.into(); 140 | let wasm_runtime_created = format!( 141 | "static WASM_RUNTIME_CREATED: &str = \"{created_datetime}\";", 142 | created_datetime = created_datetime 143 | ); 144 | 145 | let wasm_runtime_size = format!( 146 | "static WASM_RUNTIME_SIZE: &str = \"{size}\";", 147 | size = metadata.len() 148 | ); 149 | 150 | let wasm_runtime_wasmtime_version = format!( 151 | "static WASM_RUNTIME_WASMTIME_VERSION: &str = \"{wasmtime_version_number}\";", 152 | wasmtime_version_number = wasmtime_version_number 153 | ); 154 | 155 | writeln!(file, "{}", wasm_runtime_created).unwrap(); 156 | writeln!(file, "{}", wasm_runtime_size).unwrap(); 157 | writeln!(file, "{}", wasm_runtime_wasmtime_version).unwrap(); 158 | 159 | // Calculate the blake3 hash of the wasm_runtime file and write it to the wasm_runtime_resource.rs file so we can include it in the binary 160 | let wasm_runtime = fs::read(wasm_runtime_resource).unwrap(); 161 | let hash = blake3::hash(&wasm_runtime); 162 | let hash_str = format!("static WASM_RUNTIME_BLAKE3_HASH: &str = \"{}\";", hash); 163 | 164 | writeln!(file, "{}", hash_str).unwrap(); 165 | 166 | println!("cargo:rerun-if-changed=build.rs"); 167 | 168 | Ok(()) 169 | } 170 | -------------------------------------------------------------------------------- /src/hyperlight_wasm/examples/helloworld/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 The Hyperlight Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | use std::sync::{Arc, Mutex}; 18 | 19 | use examples_common::get_wasm_module_path; 20 | use hyperlight_host::HyperlightError; 21 | use hyperlight_wasm::{ParameterValue, Result, ReturnType, ReturnValue, SandboxBuilder}; 22 | 23 | fn get_time_since_boot_microsecond() -> Result { 24 | let res = std::time::SystemTime::now() 25 | .duration_since(std::time::SystemTime::UNIX_EPOCH)? 26 | .as_micros(); 27 | i64::try_from(res).map_err(HyperlightError::IntConversionFailure) 28 | } 29 | 30 | fn main() -> Result<()> { 31 | let tests = vec![ 32 | ( 33 | "HelloWorld.wasm", 34 | "HelloWorld", 35 | Some(vec![ParameterValue::String( 36 | "Message from Rust Example to Wasm Function".to_string(), 37 | )]), 38 | ), 39 | ( 40 | "HelloWorld.aot", 41 | "HelloWorld", 42 | Some(vec![ParameterValue::String( 43 | "Message from Rust Example to Wasm Function".to_string(), 44 | )]), 45 | ), 46 | ( 47 | "RunWasm.wasm", 48 | "Echo", 49 | Some(vec![ParameterValue::String( 50 | "Message from Rust Example to Wasm Function".to_string(), 51 | )]), 52 | ), 53 | ( 54 | "RunWasm.aot", 55 | "Echo", 56 | Some(vec![ParameterValue::String( 57 | "Message from Rust Example to Wasm Function".to_string(), 58 | )]), 59 | ), 60 | ]; 61 | for (idx, case) in tests.iter().enumerate() { 62 | let (mod_path, fn_name, params_opt) = case; 63 | 64 | #[cfg(all(debug_assertions, feature = "inprocess"))] 65 | let mut sandbox = SandboxBuilder::new() 66 | .with_sandbox_running_in_process() 67 | .build()?; 68 | 69 | #[cfg(not(all(debug_assertions, feature = "inprocess")))] 70 | let mut sandbox = SandboxBuilder::new().build()?; 71 | 72 | let wasm_sandbox = match mod_path.starts_with("RunWasm") { 73 | true => { 74 | let get_time_since_boot_microsecond_func = 75 | Arc::new(Mutex::new(get_time_since_boot_microsecond)); 76 | 77 | sandbox 78 | .register_host_func_0( 79 | "GetTimeSinceBootMicrosecond", 80 | &get_time_since_boot_microsecond_func, 81 | ) 82 | .unwrap(); 83 | 84 | sandbox.load_runtime()? 85 | } 86 | false => sandbox.load_runtime()?, 87 | }; 88 | 89 | let mod_path = get_wasm_module_path(mod_path)?; 90 | 91 | // Load a Wasm module into the sandbox 92 | let mut loaded_wasm_sandbox = wasm_sandbox.load_module(mod_path)?; 93 | 94 | if *fn_name == "Echo" { 95 | // Call a function in the Wasm module 96 | let ReturnValue::String(result) = loaded_wasm_sandbox.call_guest_function( 97 | fn_name, 98 | params_opt.clone(), 99 | ReturnType::String, 100 | )? 101 | else { 102 | panic!("Failed to get result from call_guest_function to Echo Function") 103 | }; 104 | println!( 105 | "Result from calling Echo Function in Wasm Module \ 106 | test case {idx}) is: {}", 107 | result 108 | ); 109 | } else if *fn_name == "HelloWorld" { 110 | // Call a function in the Wasm module 111 | let ReturnValue::Int(result) = loaded_wasm_sandbox.call_guest_function( 112 | fn_name, 113 | params_opt.clone(), 114 | ReturnType::Int, 115 | )? 116 | else { 117 | panic!("Failed to get result from call_guest_function to HelloWorld Function") 118 | }; 119 | 120 | println!( 121 | "Result from calling HelloWorld Function in Wasm Module \ 122 | test case {idx}) is: {}", 123 | result 124 | ); 125 | } 126 | } 127 | Ok(()) 128 | } 129 | -------------------------------------------------------------------------------- /src/hyperlight_wasm/examples/hostfuncs/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 The Hyperlight Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | use std::sync::{Arc, Mutex}; 18 | 19 | use examples_common::get_wasm_module_path; 20 | use hyperlight_wasm::{ParameterValue, Result, ReturnType, ReturnValue, SandboxBuilder}; 21 | 22 | fn main() { 23 | // Create a Wasm Sandbox (this is running in the local Hypervisor) 24 | 25 | let mut proto_wasm_sandbox = SandboxBuilder::new() 26 | .with_guest_input_buffer_size(0x40000) 27 | .with_guest_heap_size(0x70000) 28 | .build() 29 | .unwrap(); 30 | 31 | // Create a host-provided function and register it on the WasmSandbox. 32 | // 33 | // Note that you must wrap the function in an Arc/Mutex before you 34 | // pass it into the register_host_func_1 method below. 35 | let host_func = Arc::new(Mutex::new(|a: i32| -> Result { 36 | println!("host_func called with {}", a); 37 | Ok(a + 1) 38 | })); 39 | 40 | proto_wasm_sandbox 41 | .register_host_func_1("TestHostFunc", &host_func) 42 | .unwrap(); 43 | 44 | let wasm_sandbox = proto_wasm_sandbox.load_runtime().unwrap(); 45 | 46 | let mut loaded_wasm_sandbox = { 47 | let mod_path = get_wasm_module_path("rust_wasm_samples.wasm").unwrap(); 48 | wasm_sandbox.load_module(mod_path) 49 | } 50 | .unwrap(); 51 | 52 | let ReturnValue::Int(result) = loaded_wasm_sandbox 53 | .call_guest_function( 54 | "call_host_function", 55 | Some(vec![ParameterValue::Int(5)]), 56 | ReturnType::Int, 57 | ) 58 | .unwrap() 59 | else { 60 | panic!("Failed to get result from call_guest_function") 61 | }; 62 | 63 | println!("got result: {:?} from the host function!", result); 64 | } 65 | -------------------------------------------------------------------------------- /src/hyperlight_wasm/examples/metrics/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 The Hyperlight Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | use std::sync::{Arc, Mutex}; 18 | 19 | use examples_common::get_wasm_module_path; 20 | use hyperlight_wasm::{ParameterValue, Result, ReturnType, SandboxBuilder}; 21 | 22 | fn main() -> Result<()> { 23 | // Install prometheus metrics exporter. 24 | // We only install the metrics recorder here, but you can also use the 25 | // `metrics_exporter_prometheus::PrometheusBuilder::new().install()` method 26 | // to install a HTTP listener that serves the metrics. 27 | let prometheus_handle = metrics_exporter_prometheus::PrometheusBuilder::new() 28 | .install_recorder() 29 | .expect("Failed to install Prometheus exporter"); 30 | 31 | for _ in 0..10 { 32 | let host_func = Arc::new(Mutex::new(|a: i32| -> Result { 33 | println!("host_func called with {}", a); 34 | Ok(a + 1) 35 | })); 36 | 37 | let mut wasm_sandbox = SandboxBuilder::new() 38 | .with_guest_function_call_max_cancel_wait_millis(100) 39 | .with_guest_input_buffer_size(1000000) 40 | .build()?; 41 | 42 | wasm_sandbox.register_host_func_1("TestHostFunc", &host_func)?; 43 | 44 | let wasm_sandbox = wasm_sandbox.load_runtime()?; 45 | 46 | let mut loaded_wasm_sandbox = 47 | wasm_sandbox.load_module(get_wasm_module_path("rust_wasm_samples.wasm")?)?; 48 | 49 | loaded_wasm_sandbox 50 | .call_guest_function( 51 | "add", 52 | Some(vec![ParameterValue::Int(5), ParameterValue::Int(10)]), 53 | ReturnType::Int, 54 | ) 55 | .unwrap(); 56 | } 57 | 58 | // Render out the metrics in prometheus exposition format. 59 | // At this point, we should have created 10 of each sandbox, but 0 would be active 60 | // since they were dropped in above for-loop 61 | let payload = prometheus_handle.render(); 62 | println!("Prometheus metrics:\n{}", payload); 63 | 64 | Ok(()) 65 | } 66 | -------------------------------------------------------------------------------- /src/hyperlight_wasm/examples/rust_wasm_examples/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 The Hyperlight Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | use std::sync::{Arc, Mutex}; 18 | 19 | use examples_common::get_wasm_module_path; 20 | use hyperlight_wasm::{ParameterValue, Result, ReturnType, ReturnValue, SandboxBuilder}; 21 | 22 | fn main() -> Result<()> { 23 | let tests = [ 24 | ("hello_world", None), 25 | ( 26 | "add", 27 | Some(vec![ParameterValue::Int(5), ParameterValue::Int(3)]), 28 | ), 29 | ("call_host_function", Some(vec![ParameterValue::Int(5)])), 30 | ]; 31 | 32 | for (idx, case) in tests.iter().enumerate() { 33 | let (fn_name, params_opt) = case; 34 | let host_func = Arc::new(Mutex::new(|a: i32| -> Result { 35 | println!("host_func called with {}", a); 36 | Ok(a + 1) 37 | })); 38 | 39 | let mut proto_wasm_sandbox = SandboxBuilder::new() 40 | .with_guest_input_buffer_size(256 * 1024) 41 | .with_guest_heap_size(768 * 1024) 42 | .build()?; 43 | 44 | proto_wasm_sandbox.register_host_func_1("TestHostFunc", &host_func)?; 45 | 46 | let wasm_sandbox = proto_wasm_sandbox.load_runtime()?; 47 | let mod_path = get_wasm_module_path("rust_wasm_samples.wasm")?; 48 | 49 | // Load the Wasm module into the sandbox 50 | let mut loaded_wasm_sandbox = wasm_sandbox.load_module(mod_path)?; 51 | 52 | // Call a function in the Wasm module 53 | let ReturnValue::Int(result) = loaded_wasm_sandbox.call_guest_function( 54 | fn_name, 55 | params_opt.clone(), 56 | ReturnType::Int, 57 | )? 58 | else { 59 | panic!("Failed to get result from call_guest_function") 60 | }; 61 | 62 | println!("test case {idx} fn_name: {fn_name}\nresult: {}", result) 63 | } 64 | Ok(()) 65 | } 66 | -------------------------------------------------------------------------------- /src/hyperlight_wasm/scripts/build-wasm-examples.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | pushd %~dp0\..\..\wasmsamples 3 | call :NORMALIZEPATH "..\..\x64\%1" 4 | echo "%ABSPATH%" 5 | call compile-wasm.bat "." "%ABSPATH%" 6 | popd 7 | 8 | :: ========== FUNCTIONS ========== 9 | exit /B 10 | 11 | :NORMALIZEPATH 12 | set ABSPATH=%~f1 13 | exit /B 14 | -------------------------------------------------------------------------------- /src/hyperlight_wasm/scripts/build-wasm-examples.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -o errexit 4 | set -o nounset 5 | set -o pipefail 6 | 7 | pushd "$(dirname "${BASH_SOURCE[0]}")/../../wasmsamples" 8 | OUTPUT_DIR="../../x64/${1:-"debug"}" 9 | mkdir -p ${OUTPUT_DIR} 10 | OUTPUT_DIR=$(realpath $OUTPUT_DIR) 11 | 12 | 13 | if [ -f "/.dockerenv" ] || grep -q docker /proc/1/cgroup; then 14 | # running in a container so use the installed wasi-sdk as the devcontainer has this installed 15 | for FILENAME in $(find . -name '*.c') 16 | do 17 | echo Building ${FILENAME} 18 | # Build the wasm file with wasi-libc for wasmtime 19 | /opt/wasi-sdk/bin/clang -flto -ffunction-sections -mexec-model=reactor -O3 -z stack-size=4096 -Wl,--initial-memory=65536 -Wl,--export=__data_end -Wl,--export=__heap_base,--export=malloc,--export=free,--export=__wasm_call_ctors -Wl,--strip-all,--no-entry -Wl,--allow-undefined -Wl,--gc-sections -o ${OUTPUT_DIR}/${FILENAME%.*}-wasi-libc.wasm ${FILENAME} 20 | 21 | # Build AOT for Wasmtime; note that Wasmtime does not support 22 | # interpreting, so its wasm binary is secretly an AOT binary. 23 | cargo run -p hyperlight-wasm-aot compile ${OUTPUT_DIR}/${FILENAME%.*}-wasi-libc.wasm ${OUTPUT_DIR}/${FILENAME%.*}.aot 24 | cp ${OUTPUT_DIR}/${FILENAME%.*}.aot ${OUTPUT_DIR}/${FILENAME%.*}.wasm 25 | done 26 | else 27 | # not running in a container so use the docker image to build the wasm files 28 | echo Building docker image that has Wasm sdk. Should be quick if preivoulsy built and no changes to dockerfile. 29 | echo This will take a while if it is the first time you are building the docker image. 30 | echo Log in ${OUTPUT_DIR}/dockerbuild.log 31 | 32 | docker pull ghcr.io/hyperlight-dev/wasm-clang-builder:latest 33 | 34 | docker build --build-arg GCC_VERSION=12 --build-arg WASI_SDK_VERSION_FULL=20.0 --cache-from ghcr.io/hyperlight-dev/wasm-clang-builder:latest -t wasm-clang-builder:latest . 2> ${OUTPUT_DIR}/dockerbuild.log 35 | 36 | for FILENAME in $(find . -name '*.c') 37 | do 38 | echo Building ${FILENAME} 39 | # Build the wasm file with wasi-libc for wasmtime 40 | docker run --rm -i -v "${PWD}:/tmp/host" -v "${OUTPUT_DIR}:/tmp/output" wasm-clang-builder:latest /opt/wasi-sdk/bin/clang -flto -ffunction-sections -mexec-model=reactor -O3 -z stack-size=4096 -Wl,--initial-memory=65536 -Wl,--export=__data_end -Wl,--export=__heap_base,--export=malloc,--export=free,--export=__wasm_call_ctors -Wl,--strip-all,--no-entry -Wl,--allow-undefined -Wl,--gc-sections -o /tmp/output/${FILENAME%.*}-wasi-libc.wasm /tmp/host/${FILENAME} 41 | 42 | # Build AOT for Wasmtime; note that Wasmtime does not support 43 | # interpreting, so its wasm binary is secretly an AOT binary. 44 | cargo run -p hyperlight-wasm-aot compile ${OUTPUT_DIR}/${FILENAME%.*}-wasi-libc.wasm ${OUTPUT_DIR}/${FILENAME%.*}.aot 45 | cp ${OUTPUT_DIR}/${FILENAME%.*}.aot ${OUTPUT_DIR}/${FILENAME%.*}.wasm 46 | done 47 | fi 48 | 49 | popd 50 | -------------------------------------------------------------------------------- /src/hyperlight_wasm/src/build_info.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 The Hyperlight Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | use std::sync::Once; 18 | 19 | use log::info; 20 | // LOG_ONCE is used to log information about the crate version once 21 | static LOG_ONCE: Once = Once::new(); 22 | 23 | // The `built` crate is used in build.rs to generate a `built.rs` file that contains 24 | // information about the build environment. 25 | // 26 | // In addition build.rs appends information about the wasm_runtime binary to the `built.rs` file 27 | // 28 | // Collectively that information is used to populate the `BuildInfo` struct. 29 | // 30 | include!(concat!(env!("OUT_DIR"), "/built.rs")); 31 | 32 | /// The build information for the hyperligt-wasm crate 33 | pub struct BuildInfo { 34 | /// The date and time the wasm_runtime was built 35 | pub wasm_runtime_created: &'static str, 36 | /// The size of the wasm_runtime binary 37 | pub wasm_runtime_size: &'static str, 38 | /// The blake3 hash of the wasm_runtime binary 39 | pub wasm_runtime_blake3_hash: &'static str, 40 | /// The version of wasmtime being used by hyperlight-wasm 41 | pub wasm_runtime_wasmtime_version: &'static str, 42 | /// The name of the package 43 | pub package_name: &'static str, 44 | /// The version of the package 45 | pub package_version: &'static str, 46 | /// The features enabled for the package 47 | pub features: Vec<&'static str>, 48 | /// The target triple for the build 49 | pub target: &'static str, 50 | /// The optimization level for the build 51 | pub opt_level: &'static str, 52 | /// The profile for the build 53 | pub profile: &'static str, 54 | /// Whether the build was in debug mode 55 | pub debug: bool, 56 | /// The path to rustc used for the build 57 | pub rustc: &'static str, 58 | /// The date and time the package was built 59 | pub built_time_utc: &'static str, 60 | /// The CI platform used for the build 61 | pub ci_platform: Option<&'static str>, 62 | /// The git commit hash for the build 63 | pub git_commit_hash: Option<&'static str>, 64 | /// The git head ref for the build 65 | pub git_head_ref: Option<&'static str>, 66 | /// The git version for the build 67 | pub git_version: Option<&'static str>, 68 | /// Whether the git repo was dirty when the package was built 69 | pub git_dirty: bool, 70 | } 71 | 72 | impl Default for BuildInfo { 73 | fn default() -> Self { 74 | let mut features: Vec<&str> = Vec::new(); 75 | 76 | for feature in FEATURES { 77 | features.push(feature); 78 | } 79 | 80 | Self { 81 | wasm_runtime_created: WASM_RUNTIME_CREATED, 82 | wasm_runtime_size: WASM_RUNTIME_SIZE, 83 | wasm_runtime_blake3_hash: WASM_RUNTIME_BLAKE3_HASH, 84 | wasm_runtime_wasmtime_version: WASM_RUNTIME_WASMTIME_VERSION, 85 | package_name: PKG_NAME, 86 | package_version: PKG_VERSION, 87 | features, 88 | target: TARGET, 89 | opt_level: OPT_LEVEL, 90 | profile: PROFILE, 91 | debug: DEBUG, 92 | rustc: RUSTC, 93 | built_time_utc: BUILT_TIME_UTC, 94 | ci_platform: CI_PLATFORM, 95 | git_commit_hash: GIT_COMMIT_HASH, 96 | git_head_ref: GIT_HEAD_REF, 97 | git_version: GIT_VERSION, 98 | git_dirty: GIT_DIRTY.unwrap_or(false), 99 | } 100 | } 101 | } 102 | 103 | impl BuildInfo { 104 | /// Get the build information 105 | pub fn get() -> Self { 106 | Self::default() 107 | } 108 | pub(crate) fn log() { 109 | Self::default().log_build_details(); 110 | } 111 | fn log_build_details(&self) { 112 | LOG_ONCE.call_once(|| { 113 | info!("{}", self); 114 | }); 115 | } 116 | /// Get the version of wasmtime being used by hyperlight-wasm 117 | pub(crate) fn get_wasmtime_version() -> &'static str { 118 | WASM_RUNTIME_WASMTIME_VERSION 119 | } 120 | } 121 | 122 | impl std::fmt::Display for BuildInfo { 123 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 124 | writeln!(f, "wasm_runtime created at: {}", self.wasm_runtime_created)?; 125 | writeln!(f, "wasm_runtime size: {}", self.wasm_runtime_size)?; 126 | writeln!(f, "wasm_runtime hash: {}", self.wasm_runtime_blake3_hash)?; 127 | writeln!( 128 | f, 129 | "wasm_runtime wasmtime version: {}", 130 | self.wasm_runtime_wasmtime_version 131 | )?; 132 | writeln!(f, "Package name: {}", self.package_name)?; 133 | writeln!(f, "Package version: {}", self.package_version)?; 134 | writeln!(f, "Package features: {:#?}", self.features)?; 135 | writeln!(f, "Target triple: {}", self.target)?; 136 | writeln!(f, "Optimization level: {}", self.opt_level)?; 137 | writeln!(f, "Profile: {}", self.profile)?; 138 | writeln!(f, "Debug: {}", self.debug)?; 139 | writeln!(f, "Rustc: {}", self.rustc)?; 140 | writeln!(f, "Built at: {}", self.built_time_utc)?; 141 | writeln!(f, "CI platform: {:?}", self.ci_platform.unwrap_or("None"))?; 142 | writeln!( 143 | f, 144 | "Git commit hash: {:?}", 145 | self.git_commit_hash.unwrap_or("None") 146 | )?; 147 | writeln!(f, "Git head ref: {:?}", self.git_head_ref.unwrap_or("None"))?; 148 | writeln!(f, "Git version: {:?}", self.git_version.unwrap_or("None"))?; 149 | if self.git_dirty { 150 | writeln!(f, "Repo had uncommitted changes when built")?; 151 | } 152 | Ok(()) 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/hyperlight_wasm/src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 The Hyperlight Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #![deny(dead_code, missing_docs, unused_mut)] 18 | //! This crate provides a Hyperlight implementation for WebAssembly (Wasm) guest code. 19 | 20 | /// provides details about the build 21 | pub mod build_info; 22 | mod sandbox; 23 | 24 | use build_info::BuildInfo; 25 | use hyperlight_host::func::HostFunction1; 26 | pub use sandbox::loaded_wasm_sandbox::LoadedWasmSandbox; 27 | pub use sandbox::proto_wasm_sandbox::ProtoWasmSandbox; 28 | pub use sandbox::sandbox_builder::SandboxBuilder; 29 | pub use sandbox::wasm_sandbox::WasmSandbox; 30 | /// The container to store the value of a single parameter to a guest 31 | /// function. 32 | pub type ParameterValue = hyperlight_host::func::ParameterValue; 33 | /// The container to store the return value from a guest function call. 34 | pub type ReturnValue = hyperlight_host::func::ReturnValue; 35 | /// The type of the return value from a guest function call. 36 | pub type ReturnType = hyperlight_host::func::ReturnType; 37 | /// The Result of a fuunction call 38 | pub type Result = hyperlight_host::Result; 39 | /// Check if there is a hypervisor present 40 | pub use hyperlight_host::is_hypervisor_present; 41 | /// Set the metrics registry for hyperlight 42 | pub use hyperlight_host::metrics::set_metrics_registry; 43 | /// Create a generic HyperlightError 44 | pub use hyperlight_host::new_error; 45 | /// The function to pass to a new `WASMSandbox` to tell it how to handle 46 | /// guest requests to print some output. 47 | pub type HostPrintFn<'a> = &'a dyn HostFunction1<'a, String, i32>; 48 | 49 | /// Get the build information for this version of hyperlight-wasm 50 | pub fn get_build_info() -> BuildInfo { 51 | BuildInfo::get() 52 | } 53 | /// Get the wasmtime version used by this version of hyperlight-wasm 54 | pub fn get_wasmtime_version() -> &'static str { 55 | BuildInfo::get_wasmtime_version() 56 | } 57 | 58 | #[cfg(test)] 59 | mod tests { 60 | use std::env; 61 | use std::path::Path; 62 | 63 | // Test that the build info is correct 64 | #[test] 65 | fn test_build_info() { 66 | let build_info = super::get_build_info(); 67 | // calculate the blake3 hash of the wasm_runtime binary 68 | let wasm_runtime_hash = blake3::hash(&super::sandbox::WASM_RUNTIME); 69 | // check that the build info hash matches the wasm_runtime hash 70 | assert_eq!( 71 | build_info.wasm_runtime_blake3_hash, 72 | &wasm_runtime_hash.to_string() 73 | ); 74 | assert_eq!(build_info.package_version, env!("CARGO_PKG_VERSION")); 75 | } 76 | // Test that the wasmtime version is correct 77 | #[test] 78 | fn test_wasmtime_version() { 79 | let wasmtime_version = super::get_wasmtime_version(); 80 | // get the wasmtime version from the wasm_runtime binary's Cargo.toml 81 | let proj_dir = env::var_os("CARGO_MANIFEST_DIR").unwrap(); 82 | let cargo_toml_path = Path::new(&proj_dir) 83 | .parent() 84 | .unwrap() 85 | .join("wasm_runtime") 86 | .join("Cargo.toml"); 87 | let cargo_toml_content = 88 | std::fs::read_to_string(cargo_toml_path).expect("Failed to read Cargo.toml"); 89 | let cargo_toml: toml::Value = 90 | toml::from_str(&cargo_toml_content).expect("Failed to parse Cargo.toml"); 91 | let wasmtime_version_from_toml = cargo_toml 92 | .get("dependencies") 93 | .and_then(|deps| deps.get("wasmtime")) 94 | .and_then(|wasmtime| wasmtime.get("version")) 95 | .and_then(|version| version.as_str()) 96 | .expect("Failed to find wasmtime version in Cargo.toml"); 97 | assert_eq!(wasmtime_version, wasmtime_version_from_toml); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/hyperlight_wasm/src/sandbox/metrics.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 The Hyperlight Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | /*! 18 | This module contains the definitions and implementations of the metrics used by the sandbox module 19 | */ 20 | 21 | // Gauges, active sandboxes 22 | pub(crate) static METRIC_ACTIVE_PROTO_WASM_SANDBOXES: &str = "active_proto_wasm_sandboxes"; 23 | pub(crate) static METRIC_ACTIVE_WASM_SANDBOXES: &str = "active_wasm_sandboxes"; 24 | pub(crate) static METRIC_ACTIVE_LOADED_WASM_SANDBOXES: &str = "active_loaded_wasm_sandboxes"; 25 | 26 | // Counters, total sandboxes created during the lifetime of the process 27 | pub(crate) static METRIC_TOTAL_PROTO_WASM_SANDBOXES: &str = "proto_wasm_sandboxes_total"; 28 | pub(crate) static METRIC_TOTAL_WASM_SANDBOXES: &str = "wasm_sandboxes_total"; 29 | pub(crate) static METRIC_TOTAL_LOADED_WASM_SANDBOXES: &str = "loaded_wasm_sandboxes_total"; 30 | 31 | // Counters, total number of times loaded sandboxes have been loaded/unloaded during the lifetime of the process 32 | pub(crate) static METRIC_SANDBOX_LOADS: &str = "sandbox_loads_total"; 33 | pub(crate) static METRIC_SANDBOX_UNLOADS: &str = "sandbox_unloads_total"; 34 | 35 | #[cfg(test)] 36 | mod tests { 37 | use examples_common::get_wasm_module_path; 38 | 39 | use crate::{LoadedWasmSandbox, ProtoWasmSandbox}; 40 | 41 | #[test] 42 | #[ignore = "Needs to run separately to not get influenced by other tests"] 43 | fn test_metrics() { 44 | let recorder = metrics_util::debugging::DebuggingRecorder::new(); 45 | let snapshotter = recorder.snapshotter(); 46 | recorder.install().unwrap(); 47 | 48 | let snapshot = { 49 | let sandbox = ProtoWasmSandbox::default(); 50 | 51 | let wasm_sandbox = sandbox.load_runtime().unwrap(); 52 | let loaded_wasm_sandbox: LoadedWasmSandbox = { 53 | let mod_path = get_wasm_module_path("RunWasm.wasm").unwrap(); 54 | wasm_sandbox.load_module(mod_path).unwrap() 55 | }; 56 | loaded_wasm_sandbox.unload_module().unwrap(); 57 | snapshotter.snapshot() 58 | }; 59 | let snapshot = snapshot.into_vec(); 60 | assert_eq!(snapshot.len(), 8); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/hyperlight_wasm/src/sandbox/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 The Hyperlight Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | /// A Wasm Sandbox loaded with a module. 18 | pub(crate) mod loaded_wasm_sandbox; 19 | /// Metric definitions for Sandbox module. 20 | pub(crate) mod metrics; 21 | /// A builder for a WasmSandbox. 22 | pub(crate) mod sandbox_builder; 23 | /// A Wasm Sandbox that can load a module. 24 | pub(crate) mod wasm_sandbox; 25 | 26 | pub(crate) mod proto_wasm_sandbox; 27 | 28 | // This include! macro is replaced by the build.rs script. 29 | // The build.rs script reads the wasm_runtime binary into a static byte array named WASM_RUNTIME 30 | // contained in the wasm_runtime_resource.rs file. 31 | 32 | include!(concat!(env!("OUT_DIR"), "/wasm_runtime_resource.rs")); 33 | -------------------------------------------------------------------------------- /src/hyperlight_wasm/src/sandbox/sandbox_builder.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 The Hyperlight Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | use std::time::Duration; 18 | 19 | #[cfg(all(target_os = "windows", not(debug_assertions)))] 20 | use hyperlight_host::new_error; 21 | use hyperlight_host::sandbox::SandboxConfiguration; 22 | use hyperlight_host::{ 23 | is_hypervisor_present, GuestBinary, HyperlightError, Result, SandboxRunOptions, 24 | }; 25 | 26 | use super::proto_wasm_sandbox::ProtoWasmSandbox; 27 | use crate::HostPrintFn; 28 | 29 | // use unreasonably large minimum stack/heap/input data sizes for now to 30 | // deal with the size of wasmtime/wasi-libc aot artifacts 31 | pub const MIN_STACK_SIZE: u64 = 64 * 1024; 32 | pub const MIN_INPUT_DATA_SIZE: usize = 192 * 1024; 33 | pub const MIN_HEAP_SIZE: u64 = 1024 * 1024; 34 | pub const MIN_GUEST_ERROR_BUFFER_SIZE: usize = 1024; 35 | 36 | /// A builder for WasmSandbox 37 | #[derive(Clone)] 38 | pub struct SandboxBuilder<'a> { 39 | config: SandboxConfiguration, 40 | opts: Option, 41 | host_print_fn: Option>, 42 | #[cfg(all(target_os = "windows", debug_assertions))] 43 | guest_path: Option, 44 | } 45 | 46 | impl<'a> SandboxBuilder<'a> { 47 | /// Create a new SandboxBuilder 48 | pub fn new() -> Self { 49 | let mut config: SandboxConfiguration = Default::default(); 50 | config.set_input_data_size(MIN_INPUT_DATA_SIZE); 51 | config.set_heap_size(MIN_HEAP_SIZE); 52 | 53 | Self { 54 | config, 55 | opts: None, 56 | host_print_fn: None, 57 | #[cfg(all(target_os = "windows", debug_assertions))] 58 | guest_path: None, 59 | } 60 | } 61 | 62 | #[cfg(all(feature = "inprocess", debug_assertions))] 63 | /// Run the sandbox in-process 64 | pub fn with_sandbox_running_in_process(mut self) -> Self { 65 | self.opts = Some(SandboxRunOptions::RunInProcess(false)); 66 | self 67 | } 68 | 69 | /// Set the host print function 70 | pub fn with_host_print_fn(mut self, host_print_fn: HostPrintFn<'a>) -> Self { 71 | self.host_print_fn = Some(host_print_fn); 72 | self 73 | } 74 | 75 | /// Set the guest error buffer size 76 | /// The default (and minimum) value for this is set to the value of the MIN_GUEST_ERROR_BUFFER_SIZE const. 77 | pub fn with_guest_error_buffer_size(mut self, guest_error_buffer_size: usize) -> Self { 78 | if guest_error_buffer_size > MIN_GUEST_ERROR_BUFFER_SIZE { 79 | self.config 80 | .set_guest_error_buffer_size(MIN_GUEST_ERROR_BUFFER_SIZE); 81 | } 82 | self 83 | } 84 | 85 | /// Set the guest output buffer size 86 | pub fn with_guest_output_buffer_size(mut self, guest_output_buffer_size: usize) -> Self { 87 | self.config.set_output_data_size(guest_output_buffer_size); 88 | self 89 | } 90 | 91 | /// Set the guest input buffer size 92 | /// This is the size of the buffer that the guest can write to 93 | /// to send data to the host 94 | /// The host can read from this buffer 95 | /// The guest can write to this buffer 96 | pub fn with_guest_input_buffer_size(mut self, guest_input_buffer_size: usize) -> Self { 97 | if guest_input_buffer_size > MIN_INPUT_DATA_SIZE { 98 | self.config.set_input_data_size(guest_input_buffer_size); 99 | } 100 | self 101 | } 102 | 103 | /// Set the host function definition buffer size 104 | /// This is the size of the buffer that the host can write 105 | /// details of the host functions that the guest can call 106 | /// 107 | pub fn with_host_function_buffer_size(mut self, host_function_buffer_size: usize) -> Self { 108 | self.config 109 | .set_host_function_definition_size(host_function_buffer_size); 110 | self 111 | } 112 | 113 | /// Set the host exception buffer size 114 | /// This is the size of the buffer that the host can write to with 115 | /// details of any exceptions/errors/panics that are thrown during host function execution 116 | /// 117 | /// It allows details of any exception or error that occurred in a host function called from the guest 118 | /// to be captured and returned to the host via the guest as it is not possible to transparently return exception/error/panic 119 | /// details from a host function to the guest as the guest/host boundary is a hard boundary 120 | pub fn with_host_exception_buffer_size(mut self, host_exception_buffer_size: usize) -> Self { 121 | self.config 122 | .set_host_exception_size(host_exception_buffer_size); 123 | self 124 | } 125 | 126 | /// Set the guest stack size 127 | /// This is the size of the stack that code executing in the guest can use. 128 | /// If this value is too small then the guest will fail with a stack overflow error 129 | /// The default value (and minimum) is set to the value of the MIN_STACK_SIZE const. 130 | pub fn with_guest_stack_size(mut self, guest_stack_size: u64) -> Self { 131 | if guest_stack_size > MIN_STACK_SIZE { 132 | self.config.set_stack_size(guest_stack_size); 133 | } 134 | self 135 | } 136 | 137 | /// Set the guest heap size 138 | /// This is the size of the heap that code executing in the guest can use. 139 | /// If this value is too small then the guest will fail, usually with a malloc failed error 140 | /// The default (and minimum) value for this is set to the value of the MIN_HEAP_SIZE const. 141 | pub fn with_guest_heap_size(mut self, guest_heap_size: u64) -> Self { 142 | if guest_heap_size > MIN_HEAP_SIZE { 143 | self.config.set_heap_size(guest_heap_size); 144 | } 145 | self 146 | } 147 | 148 | /// Set the guest panic context buffer size 149 | pub fn with_guest_panic_context_buffer_size(mut self, guest_panic_buffer_size: usize) -> Self { 150 | self.config 151 | .set_guest_panic_context_buffer_size(guest_panic_buffer_size); 152 | self 153 | } 154 | 155 | /// Set the max execution time for the guest function call 156 | /// If the guest function call takes longer than this time then the guest function call will be terminated 157 | /// and the guest function will return an error 158 | pub fn with_guest_function_call_max_execution_time_millis( 159 | mut self, 160 | guest_function_call_max_execution_time: u64, 161 | ) -> Self { 162 | self.config.set_max_execution_time(Duration::from_millis( 163 | guest_function_call_max_execution_time, 164 | )); 165 | self 166 | } 167 | 168 | /// Set the max time to wait for cancellation of a guest function call 169 | /// If cancellation of the guest function call takes longer than this time then the guest function call will be terminated 170 | /// and the guest function will return an error 171 | pub fn with_guest_function_call_max_cancel_wait_millis( 172 | mut self, 173 | guest_function_call_max_cancel_wait_time: u64, 174 | ) -> Self { 175 | self.config 176 | .set_max_execution_cancel_wait_time(Duration::from_millis( 177 | guest_function_call_max_cancel_wait_time, 178 | )); 179 | self 180 | } 181 | 182 | /// Get the current configuration 183 | pub fn get_config(&self) -> &SandboxConfiguration { 184 | &self.config 185 | } 186 | 187 | /// Build the ProtoWasmSandbox 188 | pub fn build(self) -> Result { 189 | let guest_binary = match self.opts { 190 | #[cfg(target_os = "windows")] 191 | Some(SandboxRunOptions::RunInProcess(debug)) => match debug { 192 | false => GuestBinary::Buffer(super::WASM_RUNTIME.to_vec()), 193 | true => { 194 | #[cfg(debug_assertions)] 195 | { 196 | let guest_path = self.guest_path.unwrap(); 197 | GuestBinary::FilePath(guest_path) 198 | } 199 | #[cfg(not(debug_assertions))] 200 | { 201 | return Err(new_error!("Debugging is only supported in debug builds ")); 202 | } 203 | } 204 | }, 205 | _ => { 206 | if !is_hypervisor_present() { 207 | return Err(HyperlightError::NoHypervisorFound()); 208 | } 209 | GuestBinary::Buffer(super::WASM_RUNTIME.to_vec()) 210 | } 211 | }; 212 | 213 | let proto_wasm_sandbox = ProtoWasmSandbox::new( 214 | Some(self.config), 215 | self.opts, 216 | guest_binary, 217 | self.host_print_fn, 218 | )?; 219 | Ok(proto_wasm_sandbox) 220 | } 221 | } 222 | 223 | impl<'a> Default for SandboxBuilder<'a> { 224 | fn default() -> Self { 225 | Self::new() 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /src/hyperlight_wasm_aot/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hyperlight-wasm-aot" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | wasmtime = { version = "30.0.2", default-features = false, features = ["cranelift", "runtime", "component-model" ] } 8 | clap = "4.5" 9 | cargo_metadata = "0.19" 10 | -------------------------------------------------------------------------------- /src/hyperlight_wasm_aot/src/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 The Hyperlight Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | use std::path::Path; 18 | 19 | use cargo_metadata::{MetadataCommand, Package}; 20 | use clap::{Arg, Command}; 21 | use wasmtime::{Config, Engine, Module, Precompiled}; 22 | fn main() { 23 | let hyperlight_wasm_aot_version = env!("CARGO_PKG_VERSION"); 24 | let matches = Command::new("hyperlight-wasm-aot") 25 | .version(hyperlight_wasm_aot_version) 26 | .about("AOT compilation for hyperlight-wasm") 27 | .subcommand( 28 | Command::new("compile") 29 | .about("Compile a wasm file to an AOT file") 30 | .arg( 31 | Arg::new("input") 32 | .help("The input wasm file") 33 | .required(true) 34 | .index(1), 35 | ) 36 | .arg( 37 | Arg::new("output") 38 | .help("The output AOT file") 39 | .required(false) 40 | .index(2), 41 | ) 42 | .arg( 43 | Arg::new("component") 44 | .help("Compile a component rather than a module") 45 | .required(false) 46 | .long("component") 47 | .action(clap::ArgAction::SetTrue), 48 | ), 49 | ) 50 | .subcommand( 51 | Command::new("check-wasmtime-version") 52 | .about("Check the Wasmtime version use to compile a AOT file") 53 | .arg( 54 | Arg::new("file") 55 | .help("The aot compiled file to check") 56 | .required(true) 57 | .index(1), 58 | ), 59 | ) 60 | .get_matches(); 61 | 62 | match matches.subcommand_name() { 63 | Some("compile") => { 64 | let args = matches.subcommand_matches("compile").unwrap(); 65 | let infile = args.get_one::("input").unwrap(); 66 | let outfile = match args.get_one::("output") { 67 | Some(s) => s.clone(), 68 | None => { 69 | let mut path = Path::new(infile).to_path_buf(); 70 | path.set_extension("aot"); 71 | path.to_str().unwrap().to_string().clone() 72 | } 73 | }; 74 | println!("Aot Compiling {} to {}", infile, outfile); 75 | let config = get_config(); 76 | let engine = Engine::new(&config).unwrap(); 77 | let bytes = std::fs::read(infile).unwrap(); 78 | let serialized = if args.get_flag("component") { 79 | engine.precompile_component(&bytes).unwrap() 80 | } else { 81 | engine.precompile_module(&bytes).unwrap() 82 | }; 83 | std::fs::write(outfile, serialized).unwrap(); 84 | } 85 | Some("check-wasmtime-version") => { 86 | // get the wasmtime version used by hyperlight-wasm-aot 87 | let metadata = MetadataCommand::new().exec().unwrap(); 88 | let wasmtime_package: Option<&Package> = 89 | metadata.packages.iter().find(|p| p.name == "wasmtime"); 90 | let version_number = match wasmtime_package { 91 | Some(pkg) => pkg.version.clone(), 92 | None => panic!("wasmtime dependency not found"), 93 | }; 94 | let args = matches 95 | .subcommand_matches("check-wasmtime-version") 96 | .unwrap(); 97 | let file = args.get_one::("file").unwrap(); 98 | println!("Checking Wasmtime version used to compile file: {}", file); 99 | // load the file into wasmtime, check that it is aot compiled and extract the version of wasmtime used to compile it from its metadata 100 | let bytes = std::fs::read(file).unwrap(); 101 | let config = get_config(); 102 | let engine = Engine::new(&config).unwrap(); 103 | match engine.detect_precompiled(&bytes) { 104 | Some(pre_compiled) => { 105 | match pre_compiled { 106 | Precompiled::Module => { 107 | println!("The file is a valid AOT compiled Wasmtime module"); 108 | // It doesnt seem like the functions or data needed to extract the version of wasmtime used to compile the module are exposed in the wasmtime crate 109 | // so we will try and load it and then catch the error and parse the version from the error message :-( 110 | match unsafe { Module::deserialize(&engine, bytes) } { 111 | Ok(_) => println!( 112 | "File {} was AOT compiled with wasmtime version: {}", 113 | file, version_number 114 | ), 115 | Err(e) => { 116 | let error_message = e.to_string(); 117 | if !error_message.starts_with( 118 | "Module was compiled with incompatible Wasmtime version", 119 | ) { 120 | eprintln!("{}", error_message); 121 | return; 122 | } 123 | let version = error_message.trim_start_matches("Module was compiled with incompatible Wasmtime version ").trim(); 124 | println!( 125 | "File {} was AOT compiled with wasmtime version: {}", 126 | file, version 127 | ); 128 | } 129 | }; 130 | } 131 | Precompiled::Component => { 132 | eprintln!("The file is an AOT compiled Wasmtime component") 133 | } 134 | } 135 | } 136 | None => { 137 | eprintln!( 138 | "Error - {} is not a valid AOT compiled Wasmtime module or component", 139 | file 140 | ); 141 | } 142 | } 143 | } 144 | _ => { 145 | println!("No subcommand specified"); 146 | } 147 | } 148 | } 149 | 150 | fn get_config() -> Config { 151 | let mut config = Config::new(); 152 | config.target("x86_64-unknown-none").unwrap(); 153 | config.memory_reservation(0); 154 | config.memory_reservation_for_growth(0); 155 | config.memory_guard_size(0); 156 | config.guard_before_linear_memory(false); 157 | config 158 | } 159 | -------------------------------------------------------------------------------- /src/hyperlight_wasm_macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hyperlight-wasm-macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | description = """ 6 | Procedural macros to generate Hyperlight Wasm host and guest bindings from component types 7 | """ 8 | 9 | [lib] 10 | name = "hyperlight_wasm_macro" 11 | proc-macro = true 12 | 13 | [dependencies] 14 | wasmparser = { version = "0.224.0" } 15 | quote = { version = "1.0.38" } 16 | proc-macro2 = { version = "1.0.93" } 17 | syn = { version = "2.0.96" } 18 | itertools = { version = "0.14.0" } 19 | prettyplease = { version = "0.2.31" } 20 | hyperlight-component-util = { git = "https://github.com/hyperlight-dev/hyperlight", branch = "hyperlight-component-macro" } -------------------------------------------------------------------------------- /src/hyperlight_wasm_macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 The Hyperlight Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | extern crate proc_macro; 18 | 19 | use hyperlight_component_util::*; 20 | mod wasmguest; 21 | 22 | /// Create the hyperlight_guest_wasm_init() function (called by 23 | /// wasm_runtime:component.rs) for the wasm component type located at 24 | /// $HYPERLIGHT_WASM_WORLD. This function registers Hyperlight 25 | /// functions for component exports (which are implemented by calling 26 | /// into wasmtime) and registers wasmtime host functions with the 27 | /// wasmtime linker for component imports (which are implemented by 28 | /// calling to the Hyperlight host). 29 | #[proc_macro] 30 | pub fn wasm_guest_bindgen(_: proc_macro::TokenStream) -> proc_macro::TokenStream { 31 | let path = std::env::var_os("HYPERLIGHT_WASM_WORLD").unwrap(); 32 | util::read_wit_type_from_file(path, |kebab_name, ct| { 33 | let decls = emit::run_state(true, |s| { 34 | // Emit type/trait definitions for all instances in the world 35 | rtypes::emit_toplevel(s, &kebab_name, ct); 36 | // Emit the host/guest function registrations 37 | wasmguest::emit_toplevel(s, &kebab_name, ct); 38 | }); 39 | // Use util::emit_decls() to choose between emitting the token 40 | // stream directly and emitting an include!() pointing at a 41 | // temporary file, depending on whether the user has requested 42 | // a debug temporary file be created. 43 | util::emit_decls(decls).into() 44 | }) 45 | } 46 | -------------------------------------------------------------------------------- /src/rust_wasm_samples/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target="wasm32-unknown-unknown" -------------------------------------------------------------------------------- /src/rust_wasm_samples/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "rust_wasm_samples" 7 | version = "0.9.0" 8 | -------------------------------------------------------------------------------- /src/rust_wasm_samples/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust_wasm_samples" 3 | version = "0.9.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [lib] 9 | crate-type = ["cdylib"] 10 | 11 | [profile.release] 12 | lto = true 13 | opt-level = 'z' 14 | strip = true 15 | 16 | 17 | [dependencies] 18 | 19 | -------------------------------------------------------------------------------- /src/rust_wasm_samples/build.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 The Hyperlight Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | fn main() { 18 | println!("cargo:rustc-link-arg=-zstack-size=4096"); 19 | println!("cargo:rustc-link-arg=--initial-memory=65536"); 20 | println!("cargo:rustc-link-arg=--strip-all"); 21 | println!("cargo:rustc-target-feature=+bulk-memory"); 22 | } 23 | -------------------------------------------------------------------------------- /src/rust_wasm_samples/src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 The Hyperlight Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | use std::ffi::CString; 18 | mod hostfuncs { 19 | 20 | use std::os::raw::c_char; 21 | extern "C" { 22 | pub fn HostPrint(s: *const c_char) -> i32; 23 | } 24 | 25 | mod host { 26 | extern "C" { 27 | #[link_name = "TestHostFunc"] 28 | pub(super) fn test_host_func(a: i32) -> i32; 29 | } 30 | } 31 | pub(super) fn test_host_func(a: i32) -> i32 { 32 | unsafe { host::test_host_func(a) } 33 | } 34 | } 35 | 36 | macro_rules! hlprint { 37 | ($($arg:tt)*) => {{ 38 | let f = format!($($arg)*); 39 | let s = CString::new(f).unwrap(); 40 | let r; 41 | unsafe { 42 | r = hostfuncs::HostPrint(s.as_ptr()); 43 | } 44 | r 45 | }} 46 | } 47 | 48 | #[no_mangle] 49 | pub extern "C" fn hello_world() -> i32 { 50 | hlprint!("Hello from Wasm in Hyperlight!\n"); 51 | 0 52 | } 53 | 54 | #[no_mangle] 55 | pub extern "C" fn add(left: usize, right: usize) -> usize { 56 | left + right 57 | } 58 | 59 | #[no_mangle] 60 | pub extern "C" fn call_host_function(a: i32) -> i32 { 61 | hostfuncs::test_host_func(a) 62 | } 63 | -------------------------------------------------------------------------------- /src/scripts/auto-approve-dependabot.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | set -o pipefail 4 | 5 | # This script checks for open PRs from dependabot that have all checks passing and have not been 6 | # modified by another user, and approves+merges them automatically. 7 | # To be run as a GitHub action. 8 | 9 | # Check if repository argument is provided 10 | if [ -z "$1" ]; then 11 | echo "Error: Repository name not provided." 12 | echo "Usage: $0 " 13 | echo "Example: $0 hyperlight-dev/hyperlight" 14 | exit 1 15 | fi 16 | 17 | REPO="$1" 18 | echo "Checking for open Dependabot PRs to approve and merge in $REPO..." 19 | 20 | # Get all open PRs from dependabot 21 | dependabot_prs=$(gh pr list -R "$REPO" --author "dependabot[bot]" --state open --json number,title,reviews) 22 | 23 | # Exit early if no PRs found 24 | if [ -z "$dependabot_prs" ] || [ "$dependabot_prs" = "[]" ]; then 25 | echo "No open Dependabot PRs found in $REPO" 26 | exit 0 27 | fi 28 | 29 | # Count how many PRs we found 30 | pr_count=$(echo "$dependabot_prs" | jq 'length') 31 | echo "Found $pr_count open Dependabot PRs in $REPO" 32 | 33 | # Process each PR 34 | echo "$dependabot_prs" | jq -c '.[]' | while read -r pr; do 35 | pr_number=$(echo "$pr" | jq -r '.number') 36 | pr_title=$(echo "$pr" | jq -r '.title') 37 | 38 | echo "Processing PR #$pr_number: $pr_title" 39 | 40 | # Check if PR only modifies allowed files 41 | pr_files=$(gh pr view "$pr_number" -R "$REPO" --json files) 42 | invalid_files=$(echo "$pr_files" | jq -r '.files[].path' | grep -v -E '(Cargo\.toml|Cargo\.lock|\.github/workflows/.+)' || true) 43 | 44 | if [ -n "$invalid_files" ]; then 45 | echo " ❌ PR #$pr_number modifies files that are not allowed for auto-merge:" 46 | echo ${invalid_files/#/ - } 47 | echo " ℹ️ Only changes to Cargo.toml, Cargo.lock, or .github/workflows/ files are allowed" 48 | continue 49 | fi 50 | 51 | echo " ✅ PR #$pr_number only modifies allowed files (Cargo.toml, Cargo.lock, or .github/workflows/)" 52 | 53 | # First, get detailed PR information including all checks 54 | pr_details=$(gh pr view "$pr_number" -R "$REPO" --json statusCheckRollup,state) 55 | 56 | # Check if all status checks have passed (regardless of required or not) 57 | all_checks_pass=true 58 | has_pending_checks=false 59 | failed_checks="" 60 | 61 | # First identify checks that are still in progress 62 | pending_checks=$(echo "$pr_details" | jq -r '.statusCheckRollup[] | select(.status == "IN_PROGRESS" or .status == "QUEUED" or .status == "PENDING") | .name') 63 | 64 | if [ -n "$pending_checks" ]; then 65 | echo " ⏳ PR #$pr_number has pending checks:" 66 | echo "$pending_checks" | sed 's/^/ - /' 67 | echo " ℹ️ We will still approve the PR so it can merge automatically once all checks pass" 68 | has_pending_checks=true 69 | fi 70 | 71 | # Check for failed checks - only include checks that have a conclusion and are not still running 72 | # Explicitly exclude checks with status IN_PROGRESS, QUEUED, or PENDING 73 | failed_checks=$(echo "$pr_details" | jq -r '.statusCheckRollup[] | 74 | select(.conclusion != null and 75 | .conclusion != "SUCCESS" and 76 | .conclusion != "NEUTRAL" and 77 | .conclusion != "SKIPPED" and 78 | .status != "IN_PROGRESS" and 79 | .status != "QUEUED" and 80 | .status != "PENDING") | .name') 81 | 82 | if [ -n "$failed_checks" ]; then 83 | echo " ❌ PR #$pr_number has failed checks:" 84 | echo "$failed_checks" | sed 's/^/ - /' 85 | all_checks_pass=false 86 | continue 87 | fi 88 | 89 | # If we've reached here, either all checks have passed or some are pending 90 | if [ "$has_pending_checks" = false ]; then 91 | echo " ✅ All status checks passed for PR #$pr_number" 92 | fi 93 | 94 | # Check if PR has been modified by someone other than dependabot 95 | pr_commits=$(gh pr view "$pr_number" -R "$REPO" --json commits) 96 | non_dependabot_authors=$(echo "$pr_commits" | jq -r '.commits[].authors[].login' | grep -v -e "dependabot\[bot\]" -e "^$" || true) 97 | 98 | if [ -n "$non_dependabot_authors" ]; then 99 | echo " ❌ PR #$pr_number has been modified by users other than dependabot: $non_dependabot_authors" 100 | continue 101 | fi 102 | 103 | # Check if PR needs approval (i.e., hasn't been approved already) 104 | already_approved=$(echo "$pr" | jq -r '.reviews[] | select(.state == "APPROVED") | .state' | grep -c "APPROVED" || true) 105 | 106 | if [ "$already_approved" -eq 0 ]; then 107 | echo " ✅ Approving PR #$pr_number" 108 | gh pr review "$pr_number" -R "$REPO" --approve -b "Automatically approved by dependabot auto-approve workflow" 109 | else 110 | echo " ℹ️ PR #$pr_number is already approved" 111 | fi 112 | 113 | if [ "$has_pending_checks" = true ] || [ "$all_checks_pass" = true ]; then 114 | # Check if PR is up-to-date with base branch 115 | merge_status=$(gh pr view "$pr_number" -R "$REPO" --json mergeStateStatus -q '.mergeStateStatus') 116 | 117 | if [ "$merge_status" != "CLEAN" ]; then 118 | echo " ⚠️ PR #$pr_number is not up to date (status: $merge_status)" 119 | else 120 | echo " ✅ PR #$pr_number is up to date with base branch" 121 | fi 122 | 123 | # Enable auto-merge with squash strategy 124 | echo " ✅ Enabling auto-merge (squash strategy) for PR #$pr_number" 125 | gh pr merge "$pr_number" -R "$REPO" --auto --squash 126 | echo " ✅ Auto-merge enabled for PR #$pr_number" 127 | fi 128 | 129 | done 130 | 131 | echo "Finished processing Dependabot PRs for $REPO" 132 | -------------------------------------------------------------------------------- /src/wasm_runtime/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "x86_64-unknown-none" 3 | 4 | [target.x86_64-unknown-none] 5 | rustflags = [ 6 | "-C", 7 | "code-model=small", 8 | "-C", 9 | "link-args=-e entrypoint", 10 | ] 11 | linker = "rust-lld" 12 | 13 | [env] 14 | HYPERLIGHT_GUEST_TOOLCHAIN_ROOT = { value = "guest-toolchain", relative = true } 15 | 16 | [profile.release] 17 | panic = "abort" 18 | 19 | [profile.dev] 20 | opt-level = 0 21 | panic = "abort" 22 | -------------------------------------------------------------------------------- /src/wasm_runtime/.gitignore: -------------------------------------------------------------------------------- 1 | src/include/wasmtime-platform.h 2 | -------------------------------------------------------------------------------- /src/wasm_runtime/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wasm-runtime" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [[bin]] 7 | name = "wasm_runtime" 8 | path = "src/main.rs" 9 | test = false 10 | doctest = false 11 | bench = false 12 | 13 | [dependencies] 14 | hyperlight-common = { git = "https://github.com/hyperlight-dev/hyperlight", branch = "hyperlight-component-macro", default-features = false } 15 | hyperlight-guest = { git = "https://github.com/hyperlight-dev/hyperlight", branch = "hyperlight-component-macro", features = [ "printf" ] } 16 | wasmtime = { version = "30.0.2", default-features = false, features = [ "runtime", "custom-virtual-memory", "custom-native-signals", "component-model" ] } 17 | hyperlight-wasm-macro = { path = "../hyperlight_wasm_macro" } 18 | spin = "0.9.8" 19 | 20 | [build-dependencies] 21 | cc = "1.2" 22 | cfg-if = { version = "1" } 23 | cargo_metadata = "0.19" 24 | reqwest = {version = "0.12", default-features = false, features = ["blocking","rustls-tls"] } 25 | -------------------------------------------------------------------------------- /src/wasm_runtime/build.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 The Hyperlight Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | use std::fs::File; 18 | use std::io::copy; 19 | use std::path::Path; 20 | use std::{env, fs, path}; 21 | 22 | use cargo_metadata::{MetadataCommand, Package}; 23 | use reqwest::blocking::get; 24 | 25 | fn main() { 26 | println!("cargo:rerun-if-changed=."); 27 | let mut cfg = cc::Build::new(); 28 | if let Some(path) = env::var_os("PATH") { 29 | let paths: Vec<_> = env::split_paths(&path).collect(); 30 | let toolchain_path = 31 | path::PathBuf::from(env::var_os("HYPERLIGHT_GUEST_TOOLCHAIN_ROOT").unwrap()); 32 | let joined = env::join_paths(std::iter::once(toolchain_path).chain(paths)).unwrap(); 33 | env::set_var("PATH", &joined); 34 | } 35 | 36 | // Get the wasmtime_platform.h file from the appropriate wasm release 37 | 38 | // get the version of the wasmtime crate 39 | 40 | let metadata = MetadataCommand::new().exec().unwrap(); 41 | let wasmtime_package: Option<&Package> = 42 | metadata.packages.iter().find(|p| p.name == "wasmtime"); 43 | let version_number = match wasmtime_package { 44 | Some(pkg) => pkg.version.clone(), 45 | None => panic!("wasmtime dependency not found"), 46 | }; 47 | 48 | // download the wasmtime wasm_platform.h header file from GitHub releases and write it to the src/include directory 49 | 50 | // ensure the include directory exists 51 | let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); 52 | let include_file = path::Path::new(&crate_dir) 53 | .join("src") 54 | .join("include") 55 | .join("wasmtime-platform.h"); 56 | std::fs::create_dir_all(include_file.parent().unwrap()).unwrap(); 57 | 58 | // get the wasm_platform.h header file from github release with matching version e.g. https://github.com/bytecodealliance/wasmtime/releases/download/v29.0.1/wasmtime-platform.h 59 | let path = format!( 60 | "https://github.com/bytecodealliance/wasmtime/releases/download/v{}/wasmtime-platform.h", 61 | version_number 62 | ); 63 | let response = get(&path).expect("Failed to download header file"); 64 | 65 | // write the include file to the src/include directory 66 | let out_file = include_file.to_str().unwrap(); 67 | let mut dest = File::create(out_file).expect("Failed to create header file"); 68 | copy( 69 | &mut response.bytes().expect("Failed to read response").as_ref(), 70 | &mut dest, 71 | ) 72 | .expect("Failed to copy content"); 73 | 74 | // Write the version number to the metadata.rs file so that it is included in the binary 75 | 76 | let out_dir = env::var_os("OUT_DIR").unwrap(); 77 | let dest_path = Path::new(&out_dir).join("metadata.rs"); 78 | 79 | // pad out the version number string with null bytes to 32 bytes 80 | let version_number_string = format!("{:\0<32}", version_number.to_string()); 81 | 82 | let file_contents = format!( 83 | r#" 84 | // The section name beginning with .note is important, otherwise the linker will not include it in the binary. 85 | #[used] 86 | #[link_section = ".note_hyperlight_metadata"] 87 | static WASMTIME_VERSION_NUMBER: [u8; 32] = *b"{}"; 88 | "#, 89 | version_number_string 90 | ); 91 | fs::write(dest_path, file_contents).unwrap(); 92 | 93 | cfg.include("src/include"); 94 | cfg.file("src/platform.c"); 95 | cfg.compiler("clang"); 96 | if cfg!(windows) { 97 | env::set_var("AR_x86_64_unknown_none", "llvm-ar"); 98 | } 99 | cfg.compile("wasm_runtime"); 100 | 101 | println!("cargo::rerun-if-env-changed=HYPERLIGHT_WASM_WORLD"); 102 | println!("cargo::rustc-check-cfg=cfg(component)"); 103 | if env::var_os("HYPERLIGHT_WASM_WORLD").is_some() { 104 | println!("cargo::rustc-cfg=component"); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/wasm_runtime/src/component.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 The Hyperlight Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | use alloc::string::ToString; 18 | use alloc::vec; 19 | use alloc::vec::Vec; 20 | use core::result::Result::*; 21 | 22 | use hyperlight_common::flatbuffer_wrappers::function_call::FunctionCall; 23 | use hyperlight_common::flatbuffer_wrappers::function_types::{ 24 | ParameterType, ParameterValue, ReturnType, 25 | }; 26 | use hyperlight_common::flatbuffer_wrappers::guest_error::ErrorCode; 27 | use hyperlight_common::flatbuffer_wrappers::util::get_flatbuffer_result; 28 | use hyperlight_guest::error::{HyperlightGuestError, Result}; 29 | use hyperlight_guest::guest_function_definition::GuestFunctionDefinition; 30 | use hyperlight_guest::guest_function_register::register_function; 31 | use hyperlight_guest::host_function_call::call_host_function; 32 | use spin::Mutex; 33 | use wasmtime::component::{Component, Instance, Linker}; 34 | use wasmtime::{Config, Engine, Store}; 35 | 36 | static CUR_ENGINE: Mutex> = Mutex::new(None); 37 | static CUR_LINKER: Mutex>> = Mutex::new(None); 38 | static CUR_STORE: Mutex>> = Mutex::new(None); 39 | static CUR_INSTANCE: Mutex> = Mutex::new(None); 40 | 41 | hyperlight_wasm_macro::wasm_guest_bindgen!(); 42 | 43 | // dummy for compatibility with the module loading approach 44 | fn init_wasm_runtime(_function_call: &FunctionCall) -> Result> { 45 | Ok(get_flatbuffer_result::(0)) 46 | } 47 | 48 | fn load_wasm_module(function_call: &FunctionCall) -> Result> { 49 | if let ( 50 | ParameterValue::VecBytes(ref wasm_bytes), 51 | ParameterValue::Int(ref _len), 52 | Some(ref engine), 53 | ) = ( 54 | &function_call.parameters.as_ref().unwrap()[0], 55 | &function_call.parameters.as_ref().unwrap()[1], 56 | &*CUR_ENGINE.lock(), 57 | ) { 58 | let component = unsafe { Component::deserialize(engine, wasm_bytes)? }; 59 | let mut store = Store::new(engine, ()); 60 | let instance = (*CUR_LINKER.lock()) 61 | .as_ref() 62 | .unwrap() 63 | .instantiate(&mut store, &component)?; 64 | *CUR_STORE.lock() = Some(store); 65 | *CUR_INSTANCE.lock() = Some(instance); 66 | Ok(get_flatbuffer_result::(0)) 67 | } else { 68 | Err(HyperlightGuestError::new( 69 | ErrorCode::GuestFunctionParameterTypeMismatch, 70 | "Invalid parameters passed to LoadWasmModule".to_string(), 71 | )) 72 | } 73 | } 74 | 75 | #[no_mangle] 76 | pub extern "C" fn hyperlight_main() { 77 | let mut config = Config::new(); 78 | config.memory_reservation(0); 79 | config.memory_guard_size(0); 80 | config.memory_reservation_for_growth(0); 81 | config.guard_before_linear_memory(false); 82 | let engine = Engine::new(&config).unwrap(); 83 | let linker = Linker::new(&engine); 84 | *CUR_ENGINE.lock() = Some(engine); 85 | *CUR_LINKER.lock() = Some(linker); 86 | 87 | hyperlight_guest_wasm_init(); 88 | 89 | register_function(GuestFunctionDefinition::new( 90 | "InitWasmRuntime".to_string(), 91 | vec![], 92 | ReturnType::Int, 93 | init_wasm_runtime as usize, 94 | )); 95 | register_function(GuestFunctionDefinition::new( 96 | "LoadWasmModule".to_string(), 97 | vec![ParameterType::VecBytes, ParameterType::Int], 98 | ReturnType::Int, 99 | load_wasm_module as usize, 100 | )); 101 | } 102 | 103 | #[no_mangle] 104 | pub fn guest_dispatch_function(function_call: FunctionCall) -> Result> { 105 | Err(HyperlightGuestError::new( 106 | ErrorCode::GuestFunctionNotFound, 107 | function_call.function_name.clone(), 108 | )) 109 | } 110 | -------------------------------------------------------------------------------- /src/wasm_runtime/src/hostfuncs.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 The Hyperlight Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | use alloc::format; 18 | use alloc::string::ToString; 19 | use alloc::vec::Vec; 20 | 21 | use hyperlight_common::flatbuffer_wrappers::function_types::{ParameterType, ReturnType}; 22 | use hyperlight_common::flatbuffer_wrappers::guest_error::ErrorCode; 23 | use hyperlight_common::flatbuffer_wrappers::host_function_definition::HostFunctionDefinition; 24 | use hyperlight_guest::error::{HyperlightGuestError, Result}; 25 | use hyperlight_guest::host_function_call::call_host_function; 26 | use wasmtime::{Caller, Engine, FuncType, Val, ValType}; 27 | 28 | use crate::marshal; 29 | 30 | pub(crate) fn hostfunc_type(d: &HostFunctionDefinition, e: &Engine) -> Result { 31 | let mut params = Vec::new(); 32 | let mut last_was_vec = false; 33 | for p in (d.parameter_types).iter().flatten() { 34 | if last_was_vec && *p != ParameterType::Int { 35 | return Err(HyperlightGuestError::new( 36 | ErrorCode::GuestError, 37 | "Host function vector parameter missing length".to_string(), 38 | )); 39 | } 40 | 41 | params.push(match p { 42 | ParameterType::Int | ParameterType::UInt => ValType::I32, 43 | ParameterType::Long | ParameterType::ULong => ValType::I64, 44 | ParameterType::Bool => ValType::I32, 45 | ParameterType::Float => ValType::F32, 46 | ParameterType::Double => ValType::F64, 47 | ParameterType::String => ValType::I32, 48 | ParameterType::VecBytes => { 49 | last_was_vec = true; 50 | ValType::I32 51 | } 52 | }); 53 | } 54 | let mut results = Vec::new(); 55 | match d.return_type { 56 | ReturnType::Void => {} 57 | ReturnType::Int | ReturnType::UInt => results.push(ValType::I32), 58 | ReturnType::Long | ReturnType::ULong => results.push(ValType::I64), 59 | /* hyperlight_guest::host_function_call::get_host_value_return_as_{bool,float,double,string} are missing */ 60 | /* For compatibility with old host, we return 61 | * a packed i64 with a (wasm32) pointer in the lower half and 62 | * a length in the upper half. */ 63 | ReturnType::VecBytes => results.push(ValType::I64), 64 | _ => { 65 | return Err(HyperlightGuestError::new( 66 | ErrorCode::GuestError, 67 | format!( 68 | "Host function return type {:?} must be (u)int or (u)long, if present", 69 | d.return_type 70 | ), 71 | )); 72 | } 73 | } 74 | Ok(FuncType::new(e, params, results)) 75 | } 76 | 77 | pub(crate) fn call( 78 | d: &HostFunctionDefinition, 79 | mut c: Caller<'_, ()>, 80 | ps: &[Val], 81 | rs: &mut [Val], 82 | ) -> Result<()> { 83 | let params = d 84 | .parameter_types 85 | .iter() 86 | .flatten() 87 | .scan((ps.iter(), None), |s, t| { 88 | marshal::val_to_hl_param(&mut c, |c, n| c.get_export(n), s, t) 89 | }) 90 | .collect(); 91 | call_host_function(&d.function_name, Some(params), d.return_type) 92 | .expect("Host function call failed"); 93 | marshal::hl_return_to_val(&mut c, |c, n| c.get_export(n), &d.return_type, rs) 94 | } 95 | -------------------------------------------------------------------------------- /src/wasm_runtime/src/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 The Hyperlight Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #![no_std] 18 | #![no_main] 19 | 20 | extern crate alloc; 21 | 22 | mod platform; 23 | 24 | #[cfg(not(component))] 25 | mod hostfuncs; 26 | #[cfg(not(component))] 27 | mod marshal; 28 | #[cfg(not(component))] 29 | mod module; 30 | #[cfg(not(component))] 31 | mod wasip1; 32 | 33 | #[cfg(component)] 34 | mod component; 35 | #[cfg(component)] 36 | use component::*; 37 | 38 | // The file referenced in this include! macro is created by the 39 | // build.rs script. The build.rs script gets the current version of 40 | // wasmtime that this runtime binary uses, and writes it to the 41 | // metadata.rs file so that it is embedded as metadata in the 42 | // wasm_runtime binary. This allows hyperlight-wasm to then this 43 | // metadata and log it when the hyperlight-wasm crate is loaded. 44 | 45 | include!(concat!(env!("OUT_DIR"), "/metadata.rs")); 46 | -------------------------------------------------------------------------------- /src/wasm_runtime/src/module.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 The Hyperlight Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | use alloc::string::ToString; 18 | use alloc::vec::Vec; 19 | use alloc::{format, vec}; 20 | use core::ops::Deref; 21 | 22 | use hyperlight_common::flatbuffer_wrappers::function_call::FunctionCall; 23 | use hyperlight_common::flatbuffer_wrappers::function_types::{ 24 | ParameterType, ParameterValue, ReturnType, 25 | }; 26 | use hyperlight_common::flatbuffer_wrappers::guest_error::ErrorCode; 27 | use hyperlight_common::flatbuffer_wrappers::util::get_flatbuffer_result; 28 | use hyperlight_guest::error::{HyperlightGuestError, Result}; 29 | use hyperlight_guest::guest_function_definition::GuestFunctionDefinition; 30 | use hyperlight_guest::guest_function_register::register_function; 31 | use hyperlight_guest::host_function_call::print_output_as_guest_function; 32 | use hyperlight_guest::host_functions::get_host_function_details; 33 | use spin::Mutex; 34 | use wasmtime::{Config, Engine, Linker, Module, Store, Val}; 35 | 36 | use crate::{hostfuncs, marshal, wasip1}; 37 | 38 | static CUR_ENGINE: Mutex> = Mutex::new(None); 39 | static CUR_LINKER: Mutex>> = Mutex::new(None); 40 | static CUR_MODULE: Mutex> = Mutex::new(None); 41 | 42 | #[no_mangle] 43 | pub fn guest_dispatch_function(function_call: &FunctionCall) -> Result> { 44 | let engine = CUR_ENGINE.lock(); 45 | let engine = engine.deref().as_ref().ok_or(HyperlightGuestError::new( 46 | ErrorCode::GuestError, 47 | "Wasm runtime is not initialized".to_string(), 48 | ))?; 49 | let linker = CUR_LINKER.lock(); 50 | let linker = linker.deref().as_ref().ok_or(HyperlightGuestError::new( 51 | ErrorCode::GuestError, 52 | "impossible: wasm runtime has no valid linker".to_string(), 53 | ))?; 54 | let module = CUR_MODULE.lock(); 55 | let module = module.deref().as_ref().ok_or(HyperlightGuestError::new( 56 | ErrorCode::GuestError, 57 | "No wasm module loaded".to_string(), 58 | ))?; 59 | let mut store = Store::new(engine, ()); 60 | let instance = linker.instantiate(&mut store, module)?; 61 | let func = instance 62 | .get_func(&mut store, &function_call.function_name) 63 | .ok_or(HyperlightGuestError::new( 64 | ErrorCode::GuestError, 65 | "Function not found".to_string(), 66 | ))?; 67 | let mut w_params = vec![]; 68 | for f_param in (function_call.parameters) 69 | .as_ref() 70 | .unwrap_or(&vec![]) 71 | .iter() 72 | { 73 | w_params.push(marshal::hl_param_to_val( 74 | &mut store, 75 | |ctx, name| instance.get_export(ctx, name), 76 | f_param, 77 | )?); 78 | } 79 | let is_void = ReturnType::Void == function_call.expected_return_type; 80 | let n_results = if is_void { 0 } else { 1 }; 81 | let mut results = vec![Val::I32(0); n_results]; 82 | func.call(&mut store, &w_params, &mut results)?; 83 | marshal::val_to_hl_result( 84 | &mut store, 85 | |ctx, name| instance.get_export(ctx, name), 86 | function_call.expected_return_type, 87 | &results, 88 | ) 89 | } 90 | 91 | fn init_wasm_runtime() -> Result> { 92 | let mut config = Config::new(); 93 | config.memory_reservation(0); 94 | config.memory_guard_size(0); 95 | config.memory_reservation_for_growth(0); 96 | config.guard_before_linear_memory(false); 97 | let engine = Engine::new(&config)?; 98 | let mut linker = Linker::new(&engine); 99 | wasip1::register_handlers(&mut linker)?; 100 | 101 | let hostfuncs = get_host_function_details() 102 | .host_functions 103 | .unwrap_or_default(); 104 | for hostfunc in hostfuncs.iter() { 105 | let captured = hostfunc.clone(); 106 | linker.func_new( 107 | "env", 108 | &hostfunc.function_name, 109 | hostfuncs::hostfunc_type(hostfunc, &engine)?, 110 | move |c, ps, rs| { 111 | hostfuncs::call(&captured, c, ps, rs) 112 | .map_err(|e| wasmtime::Error::msg(format!("{:?}", e))) 113 | }, 114 | )?; 115 | } 116 | *CUR_ENGINE.lock() = Some(engine); 117 | *CUR_LINKER.lock() = Some(linker); 118 | Ok(get_flatbuffer_result::(0)) 119 | } 120 | 121 | fn load_wasm_module(function_call: &FunctionCall) -> Result> { 122 | if let ( 123 | ParameterValue::VecBytes(ref wasm_bytes), 124 | ParameterValue::Int(ref _len), 125 | Some(ref engine), 126 | ) = ( 127 | &function_call.parameters.as_ref().unwrap()[0], 128 | &function_call.parameters.as_ref().unwrap()[1], 129 | &*CUR_ENGINE.lock(), 130 | ) { 131 | let module = unsafe { Module::deserialize(engine, wasm_bytes)? }; 132 | *CUR_MODULE.lock() = Some(module); 133 | Ok(get_flatbuffer_result::(0)) 134 | } else { 135 | Err(HyperlightGuestError::new( 136 | ErrorCode::GuestFunctionParameterTypeMismatch, 137 | "Invalid parameters passed to LoadWasmModule".to_string(), 138 | )) 139 | } 140 | } 141 | 142 | #[no_mangle] 143 | #[allow(clippy::fn_to_numeric_cast)] // GuestFunctionDefinition expects a function pointer as i64 144 | pub extern "C" fn hyperlight_main() { 145 | register_function(GuestFunctionDefinition::new( 146 | "PrintOutput".to_string(), 147 | vec![ParameterType::String], 148 | ReturnType::Int, 149 | print_output_as_guest_function as usize, 150 | )); 151 | 152 | register_function(GuestFunctionDefinition::new( 153 | "InitWasmRuntime".to_string(), 154 | vec![], 155 | ReturnType::Int, 156 | init_wasm_runtime as usize, 157 | )); 158 | 159 | register_function(GuestFunctionDefinition::new( 160 | "LoadWasmModule".to_string(), 161 | vec![ParameterType::VecBytes, ParameterType::Int], 162 | ReturnType::Int, 163 | load_wasm_module as usize, 164 | )); 165 | } 166 | -------------------------------------------------------------------------------- /src/wasm_runtime/src/platform.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 The Hyperlight Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include 18 | #include "wasmtime-platform.h" 19 | 20 | typedef void (*wasmtime_setjmp_cb_t)(void *, void *); 21 | 22 | bool wasmtime_setjmp(const uint8_t **jmp_buf_out, 23 | bool (*callback)(uint8_t *, uint8_t *), uint8_t *payload, 24 | uint8_t *callee) 25 | { 26 | jmp_buf buf; 27 | 28 | if (setjmp(buf) != 0) { 29 | return false; 30 | } 31 | *jmp_buf_out = (uint8_t *)&buf; 32 | return callback(payload, callee); 33 | } 34 | 35 | void wasmtime_longjmp(const uint8_t *jmp_buf_ptr) { 36 | longjmp(*(jmp_buf *)jmp_buf_ptr, 1); 37 | } 38 | -------------------------------------------------------------------------------- /src/wasm_runtime/src/platform.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 The Hyperlight Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | use alloc::alloc::{alloc, dealloc, Layout}; 18 | use core::ffi::c_void; 19 | use core::sync::atomic::{AtomicPtr, Ordering}; 20 | 21 | // Wasmtime Embedding Interface 22 | 23 | /* We don't have proper support for lazy committing an mmap region, or 24 | * for setting up guard pages, because the guest doesn't have an * 25 | * appropriate interrupt handler yet. Consequently, we configure 26 | * wasmtime not to use any guard region, and precommit memory. */ 27 | #[no_mangle] 28 | pub extern "C" fn wasmtime_mmap_new(size: usize, _prot_flags: u32, ret: &mut *mut u8) -> i32 { 29 | *ret = unsafe { alloc(Layout::from_size_align(size, 0x1000).unwrap()) }; 30 | 0 31 | } 32 | 33 | /* Because of the precommitted memory strategy, we can't generally 34 | * support remap */ 35 | #[no_mangle] 36 | pub extern "C" fn wasmtime_mmap_remap(addr: *mut u8, size: usize, prot_flags: u32) -> i32 { 37 | panic!( 38 | "wasmtime_mmap_remap {:x} {:x} {:x}", 39 | addr as usize, size, prot_flags 40 | ); 41 | } 42 | 43 | #[no_mangle] 44 | pub extern "C" fn wasmtime_munmap(ptr: *mut u8, size: usize) -> i32 { 45 | unsafe { dealloc(ptr, Layout::from_size_align(size, 0x1000).unwrap()) }; 46 | 0 47 | } 48 | 49 | #[no_mangle] 50 | pub extern "C" fn wasmtime_mprotect(_ptr: *mut u8, _size: usize, prot_flags: u32) -> i32 { 51 | /* currently all memory is allocated RWX; we assume that 52 | * restricting to R or RX can be ignored */ 53 | if prot_flags == 1 || prot_flags == 3 || prot_flags == 5 { 54 | return 0; 55 | } 56 | -1 57 | } 58 | 59 | #[no_mangle] 60 | pub extern "C" fn wasmtime_page_size() -> usize { 61 | unsafe { hyperlight_guest::OS_PAGE_SIZE as usize } 62 | } 63 | 64 | #[allow(non_camel_case_types)] // we didn't choose the name! 65 | type wasmtime_trap_handler_t = 66 | extern "C" fn(ip: usize, fp: usize, has_faulting_addr: bool, faulting_addr: usize); 67 | 68 | // TODO: Correctly handle traps. 69 | #[no_mangle] 70 | pub extern "C" fn wasmtime_init_traps(_handler: wasmtime_trap_handler_t) -> i32 { 71 | 0 72 | } 73 | 74 | // The wasmtime_memory_image APIs are not yet supported. 75 | #[no_mangle] 76 | pub extern "C" fn wasmtime_memory_image_new( 77 | _ptr: *const u8, 78 | _len: usize, 79 | ret: &mut *mut c_void, 80 | ) -> i32 { 81 | *ret = core::ptr::null_mut(); 82 | 0 83 | } 84 | 85 | #[no_mangle] 86 | pub extern "C" fn wasmtime_memory_image_map_at( 87 | _image: *mut c_void, 88 | _addr: *mut u8, 89 | _len: usize, 90 | ) -> i32 { 91 | /* This should never be called because wasmtime_memory_image_new 92 | * returns NULL */ 93 | panic!("wasmtime_memory_image_map_at"); 94 | } 95 | 96 | #[no_mangle] 97 | pub extern "C" fn wasmtime_memory_image_free(_image: *mut c_void) { 98 | /* This should never be called because wasmtime_memory_image_new 99 | * returns NULL */ 100 | panic!("wasmtime_memory_image_free"); 101 | } 102 | 103 | /* Because we only have a single thread in the guest at the moment, we 104 | * don't need real thread-local storage. */ 105 | static FAKE_TLS: AtomicPtr = AtomicPtr::new(core::ptr::null_mut()); 106 | #[no_mangle] 107 | pub extern "C" fn wasmtime_tls_get() -> *mut u8 { 108 | FAKE_TLS.load(Ordering::Acquire) 109 | } 110 | #[no_mangle] 111 | pub extern "C" fn wasmtime_tls_set(ptr: *mut u8) { 112 | FAKE_TLS.store(ptr, Ordering::Release) 113 | } 114 | -------------------------------------------------------------------------------- /src/wasm_runtime/src/wasip1.rs: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 The Hyperlight Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | /// A very minimal implementation of just enough wasip1 functions for the 18 | /// things that were working in the old host to continue working 19 | use alloc::string::ToString; 20 | use alloc::vec; 21 | use alloc::vec::Vec; 22 | 23 | use hyperlight_common::flatbuffer_wrappers::function_types::{ParameterValue, ReturnType}; 24 | use hyperlight_guest::error::Result; 25 | use hyperlight_guest::host_function_call::{call_host_function, get_host_return_value}; 26 | use wasmtime::{Caller, Extern, Linker}; 27 | 28 | pub(crate) fn register_handlers(linker: &mut Linker) -> Result<()> { 29 | linker.func_wrap( 30 | "wasi_snapshot_preview1", 31 | "fd_seek", 32 | |fd: i32, filedelta: i64, whence: i32, _retptr: i32| -> i32 { 33 | panic!("fd_seek called {} {} {}", fd, filedelta, whence); 34 | }, 35 | )?; 36 | linker.func_wrap( 37 | "wasi_snapshot_preview1", 38 | "fd_write", 39 | |mut ctx: Caller<'_, T>, fd: i32, iovs: i32, iovs_len: i32, retptr: i32| { 40 | if fd != 1 { 41 | return -1; 42 | } 43 | let iovs = iovs as usize; 44 | let retptr = retptr as usize; 45 | let Some(memory) = ctx.get_export("memory").and_then(Extern::into_memory) else { 46 | return -1; 47 | }; 48 | let mut total_written: i32 = 0; 49 | for i in 0..iovs_len as usize { 50 | // iovec is size 8 51 | let iov = iovs + 8 * i; 52 | let mut bytes = [0u8; 4]; 53 | // offset 0 iovec.buf 54 | memory.read(&mut ctx, iov, &mut bytes).unwrap(); 55 | let buf = i32::from_le_bytes(bytes); 56 | // offset 4 is iovec.buf_len 57 | memory.read(&mut ctx, iov + 4, &mut bytes).unwrap(); 58 | let buf_len = i32::from_le_bytes(bytes); 59 | let mut string_bytes = vec![0u8; buf_len as usize]; 60 | memory 61 | .read(&mut ctx, buf as usize, &mut string_bytes) 62 | .unwrap(); 63 | let Ok(str) = core::str::from_utf8(&string_bytes) else { 64 | return -2; 65 | }; 66 | let Ok(()) = call_host_function( 67 | "HostPrint", 68 | Some(Vec::from(&[ParameterValue::String(str.to_string())])), 69 | ReturnType::Int, 70 | ) else { 71 | return -3; 72 | }; 73 | let Ok(written) = get_host_return_value::() else { 74 | return -4; 75 | }; 76 | total_written += written; 77 | } 78 | memory 79 | .write(&mut ctx, retptr, &total_written.to_le_bytes()) 80 | .unwrap(); 81 | 0 82 | }, 83 | )?; 84 | linker.func_wrap("wasi_snapshot_preview1", "fd_close", |fd: i32| -> i32 { 85 | panic!("fd_close called {}", fd); 86 | })?; 87 | linker.func_wrap( 88 | "wasi_snapshot_preview1", 89 | "fd_fdstat_get", 90 | |mut ctx: Caller<'_, T>, fd: i32, retptr: i32| { 91 | if fd != 1 { 92 | return -1; 93 | } 94 | if let Some(()) = (|| { 95 | let retptr = retptr as usize; 96 | let memory = ctx.get_export("memory")?.into_memory()?; 97 | // offset 0 is fdstat.fs_filetype; 4 is regular_file 98 | memory 99 | .write(&mut ctx, retptr, &(4_u16).to_le_bytes()) 100 | .unwrap(); 101 | // offset 2 is fdstat.fs_flags; 0 is no particular flags 102 | memory 103 | .write(&mut ctx, retptr + 2, &(0_u16).to_le_bytes()) 104 | .unwrap(); 105 | // offset 8 fdstat.fs_rights_base; 0b100011 is read/write/seek 106 | memory 107 | .write(&mut ctx, retptr + 8, &(0b100011_u64).to_le_bytes()) 108 | .unwrap(); 109 | // offset 8 fdstat.fs_rights_inheriting; 0b100011 is read/write/seek 110 | memory 111 | .write(&mut ctx, retptr + 16, &(0b100011_u64).to_le_bytes()) 112 | .unwrap(); 113 | Some(()) 114 | })() { 115 | 0 116 | } else { 117 | -1 118 | } 119 | }, 120 | )?; 121 | Ok(()) 122 | } 123 | -------------------------------------------------------------------------------- /src/wasmsamples/.gitignore: -------------------------------------------------------------------------------- 1 | *.wasm 2 | *.aot -------------------------------------------------------------------------------- /src/wasmsamples/HelloWorld.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 The Hyperlight Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | 20 | __attribute__((export_name("Hello"))) 21 | int Hello() 22 | { 23 | printf("Hello from Wasm in Hyperlight \n"); 24 | return 0; 25 | } 26 | 27 | 28 | __attribute__((export_name("HelloWorld"))) 29 | int HelloWorld(char* msg) 30 | { 31 | printf("%s\n", msg); 32 | 33 | char* buf = malloc(1024); 34 | if (!buf) { 35 | printf("%s", "malloc buf failed\n"); 36 | return -1; 37 | } 38 | 39 | printf("buffer address: %p\n", buf); 40 | 41 | snprintf(buf, 1024, "%s", "1234"); 42 | printf("contents of buffer after snprintf: %s\n", buf); 43 | 44 | free(buf); 45 | 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /src/wasmsamples/HostFunction.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 The Hyperlight Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include 18 | 19 | int HostFuncWithBufferAndLength(const char* buffer, int length); // Implementation of this will be available in the host 20 | 21 | __attribute__((export_name("PassBufferAndLengthToHost"))) 22 | int PassBufferAndLengthToHost() 23 | { 24 | const char* helloWorld = "Hello World!"; 25 | int helloWorldLength = 12; // Length of "Hello World!" excluding null terminator 26 | return HostFuncWithBufferAndLength(helloWorld, helloWorldLength); 27 | } 28 | -------------------------------------------------------------------------------- /src/wasmsamples/RunWasm.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 The Hyperlight Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | int HostPrint(char* msg); // Implementation of this will be available in the native host 25 | 26 | int64_t GetTimeSinceBootMicrosecond(); 27 | 28 | __attribute__((export_name("CalcFib"))) 29 | int CalcFib(int n) 30 | { 31 | if (n == 0 || n == 1) { 32 | return n; 33 | } 34 | else { 35 | return CalcFib(n - 1) + CalcFib(n - 2); 36 | } 37 | } 38 | 39 | // This function receives an array of bytes followed by a length and then returns a pointer to a buffer where the first 4 bytes is the length followed by the data. 40 | __attribute__((export_name("ReceiveByteArray"))) 41 | void* ReceiveByteArray(void* data, int length) { 42 | 43 | void * result = malloc(length + 4); 44 | memcpy(result, &length, 4); 45 | result += 4; 46 | memcpy(result, data, length); 47 | result -= 4; 48 | return result; 49 | } 50 | 51 | __attribute__((export_name("WasmPrintUsingHostPrint"))) 52 | int WasmPrintUsingHostPrint(char* msg) 53 | { 54 | // Host Print now returns a flatbuffer buffer 55 | HostPrint(msg); 56 | return strlen(msg); 57 | } 58 | 59 | __attribute__((export_name("PrintHelloWorld"))) 60 | void PrintHelloWorld() 61 | { 62 | printf("Hello World from Wasm!\n"); 63 | } 64 | 65 | 66 | __attribute__((export_name("Print"))) 67 | void Print(char* msg) 68 | { 69 | HostPrint(msg); 70 | } 71 | 72 | __attribute__((export_name("Echo"))) 73 | char* Echo(char* msg) 74 | { 75 | return msg; 76 | } 77 | 78 | __attribute__((export_name("ToUpper"), optnone)) 79 | char* ToUpper(char* msg) 80 | { 81 | int len = strlen(msg); 82 | if (len) 83 | { 84 | char* ret = msg; 85 | while (*msg) 86 | { 87 | *msg = toupper((unsigned char)*msg); 88 | msg++; 89 | } 90 | return ret; 91 | } 92 | return 0; 93 | } 94 | 95 | __attribute__((export_name("PrintUpper"), optnone)) 96 | void PrintUpper(char* msg) 97 | { 98 | HostPrint(ToUpper(msg)); 99 | } 100 | 101 | __attribute__((export_name("KeepCPUBusy"))) 102 | int KeepCPUBusy(int ms) 103 | { 104 | int64_t start, end; 105 | double duration = 0.0; 106 | /* Store start time here */ 107 | start = GetTimeSinceBootMicrosecond(); 108 | int iter = 0; 109 | int fib = 0; 110 | 111 | while (1) { 112 | fib = CalcFib(10); 113 | end = GetTimeSinceBootMicrosecond(); 114 | duration = (double)(end - start) / 1000.0; 115 | if (duration >= ms) { 116 | break; 117 | } 118 | iter++; 119 | if (iter == INT_MAX) { 120 | printf("Reached int max -reset i"); 121 | iter = 0; 122 | } 123 | } 124 | 125 | printf("Kept CPU busy for %d ms using %d iterations of fib(10) %d|toreach max = %d|", ms, iter, INT_MAX, INT_MAX-iter); 126 | return ms; 127 | } 128 | -------------------------------------------------------------------------------- /src/wasmsamples/compile-wasm.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | SETLOCAL EnableDelayedExpansion 3 | IF "%~1" == "" GOTO Error 4 | IF "%~2" == "" GOTO Error 5 | 6 | 7 | set "dockercmd=docker" 8 | set "dockerinput=%~1" 9 | set "dockeroutput=%~2" 10 | 11 | where docker || ( 12 | set "dockercmd=wsl docker" 13 | set "dockerinput=$(wslpath '%~1')" 14 | set "dockeroutput=$(wslpath '%~2')" 15 | ) 16 | 17 | %dockercmd% pull ghcr.io/hyperlight-dev/wasm-clang-builder:latest 18 | 19 | echo Building docker image that has Wasm sdk. Should be quick if no changes to docker image. 20 | echo Log in %2\dockerbuild.log 21 | %dockercmd% build --build-arg GCC_VERSION=12 --build-arg WASI_SDK_VERSION_FULL=20.0 --cache-from ghcr.io/hyperlight-dev/wasm-clang-builder:latest -t wasm-clang-builder:latest !dockerinput! 2> %2dockerbuild.log 22 | 23 | echo Building Wasm files in %1 and output to %2 24 | for /R "%1" %%i in (*.c) do ( 25 | echo %%~ni.c 26 | %dockercmd% run --rm -i -v !dockerinput!:/tmp/host1 -v !dockeroutput!/:/tmp/host2 wasm-clang-builder /opt/wasi-sdk/bin/clang -flto -ffunction-sections -mexec-model=reactor -O3 -z stack-size=4096 -Wl,--initial-memory=65536 -Wl,--export=__data_end -Wl,--export=__heap_base,--export=malloc,--export=free,--export=__wasm_call_ctors -Wl,--strip-all,--no-entry -Wl,--allow-undefined -Wl,--gc-sections -o /tmp/host2/%%~ni.wasm /tmp/host1/%%~ni.c 27 | echo %2\%%~ni.wasm 28 | rem Build AOT for Wasmtime; note that Wasmtime does not support 29 | rem interpreting, so its wasm binary is secretly an AOT binary. 30 | cargo run -p hyperlight-wasm-aot compile %2\%%~ni.wasm %2\%%~ni.aot 31 | copy %2\%%~ni.aot %2\%%~ni.wasm 32 | ) 33 | 34 | goto :EOF 35 | :Error 36 | echo Usage - compile-wasm ^ ^ 37 | -------------------------------------------------------------------------------- /src/wasmsamples/dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu 2 | ENV DEBIAN_FRONTEND=noninteractive 3 | LABEL org.opencontainers.image.source=https://github.com/hyperlight-dev/hyperlight-wasm 4 | 5 | ARG GCC_VERSION=12 6 | 7 | RUN apt-get update \ 8 | && apt-get upgrade -y 9 | RUN apt-get install -y wget \ 10 | && apt-get install -y build-essential \ 11 | && apt-get install -y g++-multilib \ 12 | && apt-get install -y libgcc-${GCC_VERSION}-dev \ 13 | && apt-get install -y lib32gcc-${GCC_VERSION}-dev 14 | 15 | ARG WASI_SDK_VERSION_FULL=20.0 16 | ARG WASI_SDK_VERSION_MAJOR=${WASI_SDK_VERSION_FULL%%.*} 17 | 18 | RUN wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-${WASI_SDK_VERSION_MAJOR}/wasi-sdk-${WASI_SDK_VERSION_FULL}-linux.tar.gz \ 19 | && tar xvf wasi-sdk-${WASI_SDK_VERSION_FULL}-linux.tar.gz \ 20 | && rm wasi-sdk-${WASI_SDK_VERSION_FULL}-linux.tar.gz \ 21 | && mv /wasi-sdk-${WASI_SDK_VERSION_FULL} /opt/wasi-sdk 22 | CMD ["/bin/sh"] 23 | -------------------------------------------------------------------------------- /typos.toml: -------------------------------------------------------------------------------- 1 | [default] 2 | extend-ignore-identifiers-re = ["Fo"] 3 | 4 | [files] 5 | extend-exclude = [] 6 | 7 | [default.extend-words] 8 | 9 | --------------------------------------------------------------------------------