├── .clang-format ├── .editorconfig ├── .github ├── codecov.yml ├── dependabot.yml ├── scripts │ └── build-libbpf.sh └── workflows │ ├── dependency-review.yml │ ├── fuzzing.yml │ ├── main.yml │ ├── posix.yml │ ├── scorecards.yml │ ├── update-docs.yml │ └── windows.yml ├── .gitignore ├── .gitmodules ├── .pre-commit-config.yaml ├── .travis.yml ├── CMakeLists.txt ├── Doxyfile ├── LICENSE.txt ├── README.md ├── aarch64_test ├── CMakeLists.txt ├── run-interpret.sh └── run-jit.sh ├── bin ├── ubpf-assembler └── ubpf-disassembler ├── bpf ├── CMakeLists.txt ├── bpf.h ├── map.bpf.c └── rel_64_32.bpf.c ├── cmake ├── arm64.cmake ├── options.cmake ├── packaging.cmake ├── platform.cmake ├── settings.cmake └── version.cmake ├── compat └── macOS │ ├── elfdefinitions.h │ ├── gelf.h │ ├── libelf.h │ └── sys │ └── elfdefinitions.h ├── custom_tests ├── CMakeLists.txt ├── README.md ├── data │ ├── ubpf_test_atomic_validate.input │ ├── ubpf_test_custom_local_function_stack_size.input │ ├── ubpf_test_custom_local_function_stack_size_zero.input │ ├── ubpf_test_debug_function.input │ ├── ubpf_test_default_dispatcher_helper_context.input │ ├── ubpf_test_default_local_function_stack_size.input │ ├── ubpf_test_external_dispatcher_context_overwrite.input │ ├── ubpf_test_external_dispatcher_simple_context.input │ ├── ubpf_test_frame_pointer.input │ ├── ubpf_test_jit_unexpected_instruction.input │ ├── ubpf_test_update_dispatcher.input │ └── ubpf_test_update_helpers.input ├── descrs │ ├── ubpf_test_atomic_validate.md │ ├── ubpf_test_custom_local_function_stack_size.md │ ├── ubpf_test_custom_local_function_stack_size_unaligned.md │ ├── ubpf_test_custom_local_function_stack_size_zero.md │ ├── ubpf_test_debug_function.md │ ├── ubpf_test_default_dispatcher_helper_context.md │ ├── ubpf_test_default_local_function_stack_size.md │ ├── ubpf_test_external_dispatcher_context_overwrite.md │ ├── ubpf_test_external_dispatcher_simple_context.md │ ├── ubpf_test_frame_pointer.md │ ├── ubpf_test_jit_buffer_too_small.md │ ├── ubpf_test_jit_unexpected_instruction.md │ ├── ubpf_test_update_dispatcher.md │ └── ubpf_test_update_helpers.md └── srcs │ ├── test_helpers.h │ ├── ubpf_custom_test_support.cc │ ├── ubpf_custom_test_support.h │ ├── ubpf_test_atomic_validate.cc │ ├── ubpf_test_custom_local_function_stack_size.cc │ ├── ubpf_test_custom_local_function_stack_size_unaligned.cc │ ├── ubpf_test_custom_local_function_stack_size_zero.cc │ ├── ubpf_test_debug_function.cc │ ├── ubpf_test_default_dispatcher_helper_context.cc │ ├── ubpf_test_default_local_function_stack_size.cc │ ├── ubpf_test_external_dispatcher_context_overwrite.cc │ ├── ubpf_test_external_dispatcher_simple_context.cc │ ├── ubpf_test_frame_pointer.cc │ ├── ubpf_test_jit_buffer_too_small.cc │ ├── ubpf_test_jit_unexpected_instruction.cc │ ├── ubpf_test_update_dispatcher.cc │ └── ubpf_test_update_helpers.cc ├── docs └── Contributing.md ├── libfuzzer ├── CMakeLists.txt ├── README.md ├── libfuzz_harness.cc ├── libfuzzer_config.h.inc └── split.sh ├── mainpage.dox ├── requirements.txt ├── scripts ├── .check-license.ignore ├── build-libbpf.sh ├── check-license.sh ├── commit-msg ├── format-code ├── format-code.ps1 └── pre-commit ├── test_framework ├── expand-testcase.py ├── test_assembler.py ├── test_disassembler.py ├── test_elf.py ├── test_jit.py ├── test_roundtrip.py ├── test_vm.py └── testdata.py ├── tests ├── add.data ├── add64.data ├── alu-arith.data ├── alu-bit.data ├── alu.data ├── alu64-arith.data ├── alu64-bit.data ├── alu64.data ├── arsh-reg.data ├── arsh.data ├── arsh32-high-shift.data ├── arsh64.data ├── be16-high.data ├── be16.data ├── be32-high.data ├── be32.data ├── be64.data ├── call-memfrob.data ├── call-save.data ├── call.data ├── call_local_use_stack.data ├── call_unwind.data ├── call_unwind_fail.data ├── div-by-zero-imm.data ├── div-by-zero-reg.data ├── div32-by-zero-reg.data ├── div32-high-divisor.data ├── div32-imm.data ├── div32-reg.data ├── div64-by-zero-imm.data ├── div64-by-zero-reg.data ├── div64-imm.data ├── div64-negative-imm.data ├── div64-negative-reg.data ├── div64-reg.data ├── early-exit.data ├── elf │ ├── bad-rel-offset.data │ ├── bad-rel-strtab-index.data │ ├── bad-rel-symbol-index.data │ ├── bad-rel-symbol-name.data │ ├── bad-rel-symbol-table-section-index.data │ ├── bad-rel-type.data │ ├── bad-section-header-offset.data │ ├── bad-section-header-size.data │ ├── bad-section-offset.data │ ├── bad-section-size.data │ ├── ehdr-short.data │ ├── no-text-section.data │ ├── ok.data │ ├── rel-sym-not-found.data │ ├── too-many-sections.data │ ├── wrong-byte-order.data │ ├── wrong-class.data │ ├── wrong-machine.data │ ├── wrong-osabi.data │ ├── wrong-type.data │ └── wrong-version.data ├── err-call-bad-imm.data ├── err-call-invalid-jump.data ├── err-call-unreg.data ├── err-call0.data ├── err-endian-size.data ├── err-incomplete-lddw.data ├── err-incomplete-lddw2.data ├── err-infinite-loop.data ├── err-invalid-reg-dst.data ├── err-invalid-reg-src.data ├── err-jmp-lddw.data ├── err-jmp-out.data ├── err-lddw-invalid-src.data ├── err-stack-oob.data ├── err-too-many-instructions.data ├── err-unknown-opcode.data ├── err-write-r10.dst ├── exit-not-last.data ├── exit.data ├── factorial.data ├── ja.data ├── jeq-imm.data ├── jeq-reg.data ├── jge-imm.data ├── jgt-imm.data ├── jgt-reg.data ├── jit-bounce.data ├── jle-imm.data ├── jle-reg.data ├── jlt-imm.data ├── jlt-reg.data ├── jmp.data ├── jne-reg.data ├── jset-imm.data ├── jset-reg.data ├── jsge-imm.data ├── jsge-reg.data ├── jsgt-imm.data ├── jsgt-reg.data ├── jsle-imm.data ├── jsle-reg.data ├── jslt-imm.data ├── jslt-reg.data ├── lddw.data ├── lddw2.data ├── ldx.data ├── ldxb-0offset.data ├── ldxb-all.data ├── ldxb-large-offset.data ├── ldxb-small-offset.data ├── ldxb.data ├── ldxdw.data ├── ldxh-all.data ├── ldxh-all2.data ├── ldxh-same-reg.data ├── ldxh.data ├── ldxw-all.data ├── ldxw.data ├── le16.data ├── le32.data ├── le64.data ├── lsh-reg.data ├── mem-len.data ├── mod-by-zero-imm.data ├── mod-by-zero-reg.data ├── mod.data ├── mod32.data ├── mod64-by-zero-imm.data ├── mod64-by-zero-reg.data ├── mod64.data ├── mov.data ├── mov64-sign-extend.data ├── mul-loop-memory-iterations.data ├── mul-loop.data ├── mul32-imm.data ├── mul32-reg-overflow.data ├── mul32-reg.data ├── mul64-imm.data ├── mul64-reg.data ├── neg.data ├── neg64.data ├── prime.data ├── reload.data ├── rsh-reg.data ├── rsh32.data ├── st.data ├── stack.data ├── stack2.data ├── stack3.data ├── stb.data ├── stdw.data ├── sth.data ├── string-stack.data ├── stw.data ├── stx.data ├── stxb-all.data ├── stxb-all2.data ├── stxb-chain.data ├── stxb.data ├── stxdw.data ├── stxh.data ├── stxw.data ├── subnet.data ├── tcp-port-80 │ ├── match.data │ ├── nomatch-ethertype.data │ ├── nomatch-proto.data │ ├── nomatch.data │ └── tcp-port-80.asm ├── tcp-sack │ ├── .gitignore │ ├── match.data │ ├── nomatch.data │ ├── pkt-nosack.hex │ ├── pkt-sack.hex │ ├── tcp-sack.asm │ └── tcp-sack.c └── unload_reload.data ├── ubpf ├── __init__.py ├── asm_parser.py ├── assembler.py ├── dictionary_generator.py └── disassembler.py ├── ubpf_plugin ├── CMakeLists.txt ├── test_helpers.h └── ubpf_plugin.cc └── vm ├── .gitignore ├── CMakeLists.txt ├── compat ├── CMakeLists.txt ├── libraries │ ├── CMakeLists.txt │ └── win-c │ │ └── CMakeLists.txt ├── macos │ └── endian.h └── windows │ ├── endian.h │ ├── sys │ ├── mman.c │ └── mman.h │ ├── unistd.c │ └── unistd.h ├── ebpf.h ├── inc └── ubpf.h ├── test.c ├── ubpf_config.h.inc ├── ubpf_instruction_valid.c ├── ubpf_int.h ├── ubpf_jit.c ├── ubpf_jit_arm64.c ├── ubpf_jit_support.c ├── ubpf_jit_support.h ├── ubpf_jit_x86_64.c ├── ubpf_loader.c └── ubpf_vm.c /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | IndentWidth: 4 3 | ColumnLimit: 120 4 | AlignEscapedNewlines: Left 5 | AlignAfterOpenBracket: AlwaysBreak 6 | # 7 | # Bind * to the type rather than the name. 8 | PointerAlignment: Left 9 | # 10 | # Put function name on separate line from return type. 11 | AlwaysBreakAfterReturnType: All 12 | # 13 | # Put arguments either all on same line or on separate lines. 14 | BinPackArguments: false 15 | # 16 | # Put function parameters on separate lines. 17 | BinPackParameters: false 18 | # 19 | # Open brace goes on new line only when starting a new struct, enum, or func. 20 | BreakBeforeBraces: Mozilla 21 | # 22 | # Don't sort includes in alphabetical order because Windows headers are odd. 23 | SortIncludes: false -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022-present, IO Visor Project 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | # top-most EditorConfig file 5 | root = true 6 | 7 | [*] 8 | indent_style = space 9 | indent_size = 4 10 | charset = utf-8 11 | trim_trailing_whitespace = true 12 | insert_final_newline = true 13 | -------------------------------------------------------------------------------- /.github/codecov.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation 2 | # SPDX-License-Identifier: MIT 3 | 4 | codecov: 5 | notify: 6 | wait_for_ci: yes 7 | coverage: 8 | status: 9 | project: 10 | default: 11 | threshold: 2% 12 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022-present, IO Visor Project 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | # To get started with Dependabot version updates, you'll need to specify which 5 | # package ecosystems to update and where the package manifests are located. 6 | # Please see the documentation for all configuration options: 7 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 8 | 9 | version: 2 10 | updates: 11 | - package-ecosystem: "github-actions" 12 | # Workflow files stored in the 13 | # default location of `.github/workflows` 14 | directory: "/" 15 | schedule: 16 | interval: "weekly" 17 | day: "saturday" 18 | groups: 19 | actions: 20 | patterns: 21 | - "*" 22 | 23 | - package-ecosystem: "gitsubmodule" 24 | directory: "/" 25 | schedule: 26 | interval: "weekly" 27 | day: "saturday" 28 | groups: 29 | actions: 30 | patterns: 31 | - "*" 32 | 33 | - package-ecosystem: "nuget" 34 | directory: "/" 35 | schedule: 36 | interval: "weekly" 37 | day: "saturday" 38 | groups: 39 | actions: 40 | patterns: 41 | - "*" 42 | 43 | - package-ecosystem: "pip" 44 | directory: "/" 45 | schedule: 46 | interval: "weekly" 47 | day: "saturday" 48 | groups: 49 | actions: 50 | patterns: 51 | - "*" 52 | -------------------------------------------------------------------------------- /.github/scripts/build-libbpf.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | # Copyright (c) Microsoft Corporation 3 | # SPDX-License-Identifier: MIT 4 | 5 | git clone https://github.com/libbpf/libbpf.git 6 | if [ $? -ne 0 ]; then 7 | echo "Could not clone the libbpf repository." 8 | exit 1 9 | fi 10 | 11 | # Jump in to the src directory to do the actual build. 12 | cd libbpf/src 13 | 14 | make 15 | if [ $? -ne 0 ]; then 16 | echo "Could not build libbpf source." 17 | exit 1 18 | fi 19 | 20 | # Now that the build was successful, install the library (shared 21 | # object and header files) in a spot where FindLibBpf.cmake can 22 | # find it when it is being built. 23 | sudo PREFIX=/usr LIBDIR=/usr/lib/x86_64-linux-gnu/ make install 24 | exit 0 25 | -------------------------------------------------------------------------------- /.github/workflows/dependency-review.yml: -------------------------------------------------------------------------------- 1 | # Dependency Review Action 2 | # 3 | # This Action will scan dependency manifest files that change as part of a Pull Request, 4 | # surfacing known-vulnerable versions of the packages declared or updated in the PR. 5 | # Once installed, if the workflow run is marked as required, 6 | # PRs introducing known-vulnerable packages will be blocked from merging. 7 | # 8 | # Source repository: https://github.com/actions/dependency-review-action 9 | name: 'Dependency Review' 10 | on: [pull_request] 11 | 12 | permissions: 13 | contents: read 14 | 15 | jobs: 16 | dependency-review: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: Harden Runner 20 | uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0 21 | with: 22 | egress-policy: audit 23 | 24 | - name: 'Checkout Repository' 25 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 26 | - name: 'Dependency Review' 27 | uses: actions/dependency-review-action@da24556b548a50705dd671f47852072ea4c105d9 # v4.7.1 28 | -------------------------------------------------------------------------------- /.github/workflows/scorecards.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. They are provided 2 | # by a third-party and are governed by separate terms of service, privacy 3 | # policy, and support documentation. 4 | 5 | name: Scorecard supply-chain security 6 | on: 7 | # For Branch-Protection check. Only the default branch is supported. See 8 | # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection 9 | branch_protection_rule: 10 | # To guarantee Maintained check is occasionally updated. See 11 | # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained 12 | schedule: 13 | - cron: '20 7 * * 2' 14 | push: 15 | branches: ["main"] 16 | 17 | # Declare default permissions as read only. 18 | permissions: read-all 19 | 20 | jobs: 21 | analysis: 22 | name: Scorecard analysis 23 | runs-on: ubuntu-latest 24 | permissions: 25 | # Needed to upload the results to code-scanning dashboard. 26 | security-events: write 27 | # Needed to publish results and get a badge (see publish_results below). 28 | id-token: write 29 | contents: read 30 | actions: read 31 | 32 | steps: 33 | - name: Harden Runner 34 | uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0 35 | with: 36 | egress-policy: audit 37 | 38 | - name: "Checkout code" 39 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 40 | with: 41 | persist-credentials: false 42 | 43 | - name: "Run analysis" 44 | uses: ossf/scorecard-action@f49aabe0b5af0936a0987cfb85d86b75731b0186 # v2.4.1 45 | with: 46 | results_file: results.sarif 47 | results_format: sarif 48 | # (Optional) "write" PAT token. Uncomment the `repo_token` line below if: 49 | # - you want to enable the Branch-Protection check on a *public* repository, or 50 | # - you are installing Scorecards on a *private* repository 51 | # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat. 52 | # repo_token: ${{ secrets.SCORECARD_TOKEN }} 53 | 54 | # Public repositories: 55 | # - Publish results to OpenSSF REST API for easy access by consumers 56 | # - Allows the repository to include the Scorecard badge. 57 | # - See https://github.com/ossf/scorecard-action#publishing-results. 58 | # For private repositories: 59 | # - `publish_results` will always be set to `false`, regardless 60 | # of the value entered here. 61 | publish_results: true 62 | 63 | # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF 64 | # format to the repository Actions tab. 65 | - name: "Upload artifact" 66 | uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 67 | with: 68 | name: SARIF file 69 | path: results.sarif 70 | retention-days: 5 71 | 72 | # Upload the results to GitHub's code scanning dashboard. 73 | - name: "Upload to code-scanning" 74 | uses: github/codeql-action/upload-sarif@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 75 | with: 76 | sarif_file: results.sarif 77 | -------------------------------------------------------------------------------- /.github/workflows/update-docs.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | # This action will run doxygen to update the documentation at https://iovisor.github.io/ubpf/ 5 | # which is a view of the gh-pages branch. This action is done whenever the main branch is updated. 6 | # For docs on gh-pages see https://pages.github.com/ 7 | # 8 | # The following two links discuss steps similar to this action so may be useful reading 9 | # to understand how the automatic update works: 10 | # https://growworkinghard.altervista.org/doxygen-documentation-on-github-using-gh-pages/ 11 | # https://github.com/m-a-d-n-e-s-s/madness/issues/104 12 | 13 | name: Doxygen Action 14 | 15 | # Controls when the action will run. Triggers the workflow on push # events 16 | # but only for the main branch 17 | on: 18 | push: 19 | branches: [ main ] 20 | 21 | permissions: 22 | contents: read 23 | 24 | jobs: 25 | build: 26 | permissions: 27 | contents: write # for Git to git push 28 | runs-on: ubuntu-latest 29 | 30 | steps: 31 | 32 | - name: Harden Runner 33 | uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 34 | with: 35 | egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs 36 | 37 | - name: Install doxygen 38 | run: | 39 | sudo apt install doxygen 40 | 41 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 42 | 43 | - name: Clone docs 44 | run: | 45 | git config --global user.email 'ubpf@users.noreply.github.com' 46 | git config --global user.name 'Github Action' 47 | git clone --branch gh-pages https://github.com/${{github.repository}}.git docs/html 48 | 49 | - name: Update docs 50 | run: | 51 | doxygen 52 | cd docs/html 53 | git add . 54 | if [ -n "$(git status --porcelain)" ]; then 55 | git commit -s -m "Updated documentation" 56 | git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{github.repository}}.git 57 | git push 58 | fi 59 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | build_fuzzer/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | 53 | # Translations 54 | *.mo 55 | *.pot 56 | 57 | # Django stuff: 58 | *.log 59 | local_settings.py 60 | db.sqlite3 61 | 62 | # Flask stuff: 63 | instance/ 64 | .webassets-cache 65 | 66 | # Scrapy stuff: 67 | .scrapy 68 | 69 | # Sphinx documentation 70 | docs/_build/ 71 | 72 | # PyBuilder 73 | target/ 74 | 75 | # Jupyter Notebook 76 | .ipynb_checkpoints 77 | 78 | # IPython 79 | profile_default/ 80 | ipython_config.py 81 | 82 | # pyenv 83 | .python-version 84 | 85 | # celery beat schedule file 86 | celerybeat-schedule 87 | 88 | # SageMath parsed files 89 | *.sage.py 90 | 91 | # Environments 92 | .env 93 | .venv 94 | env/ 95 | venv/ 96 | ENV/ 97 | env.bak/ 98 | venv.bak/ 99 | 100 | # Spyder project settings 101 | .spyderproject 102 | .spyproject 103 | 104 | # Rope project settings 105 | .ropeproject 106 | 107 | # mkdocs documentation 108 | /site 109 | 110 | # mypy 111 | .mypy_cache/ 112 | .dmypy.json 113 | dmypy.json 114 | 115 | # Pyre type checker 116 | .pyre/ 117 | 118 | .vscode/ 119 | .vs/ -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "vm/compat/libraries/win-c/src"] 2 | path = vm/compat/libraries/win-c/src 3 | url = https://github.com/takamin/win-c 4 | [submodule "external/bpf_conformance"] 5 | path = external/bpf_conformance 6 | url = https://github.com/Alan-Jowett/bpf_conformance.git 7 | [submodule "external/ebpf-verifier"] 8 | path = external/ebpf-verifier 9 | url = https://github.com/alan-jowett/ebpf-verifier.git 10 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/gitleaks/gitleaks 3 | rev: v8.16.3 4 | hooks: 5 | - id: gitleaks 6 | - repo: https://github.com/jumanjihouse/pre-commit-hooks 7 | rev: 3.0.0 8 | hooks: 9 | - id: shellcheck 10 | - repo: https://github.com/pocc/pre-commit-hooks 11 | rev: v1.3.5 12 | hooks: 13 | - id: cpplint 14 | - repo: https://github.com/pre-commit/pre-commit-hooks 15 | rev: v4.4.0 16 | hooks: 17 | - id: end-of-file-fixer 18 | - id: trailing-whitespace 19 | - repo: https://github.com/pylint-dev/pylint 20 | rev: v2.17.2 21 | hooks: 22 | - id: pylint 23 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | compiler: 3 | - gcc 4 | os: linux 5 | dist: xenial 6 | jobs: 7 | include: 8 | - name: python 2.7-amd64 9 | env: PYTHON=python2 10 | arch: amd64 11 | before_install: 12 | - sudo apt-get update 13 | - sudo apt-get -y install python python-pip python-setuptools python-wheel 14 | after_success: 15 | - coveralls --gcov-options '\-lp' -i $PWD/vm/ubpf_vm.c -i $PWD/vm/ubpf_jit_x86_64.c -i $PWD/vm/ubpf_loader.c 16 | - name: python-2.7-arm64 17 | env: PYTHON=python2 18 | arch: arm64 19 | before_install: 20 | - sudo apt-get update 21 | - sudo apt-get -y install python python-dev python-pip python-setuptools python-wheel libffi-dev libssl-dev 22 | install: 23 | - $PYTHON -m pip install --upgrade "pip<21.0" 24 | - $PYTHON -m pip install -r requirements.txt 25 | - $PYTHON -m pip install "cryptography<3.3" 26 | - $PYTHON -m pip install "pyopenssl<21.0.0" 27 | - $PYTHON -m pip install cpp-coveralls 28 | after_success: 29 | - coveralls --gcov-options '\-lp' -i $PWD/vm/ubpf_vm.c -i $PWD/vm/ubpf_jit_arm64.c -i $PWD/vm/ubpf_loader.c 30 | - name: python 3.5 31 | env: PYTHON=python3 32 | before_install: 33 | - sudo apt-get update 34 | - sudo apt-get -y install python3 python3-pip python3-setuptools python3-wheel 35 | # command to install dependencies 36 | install: 37 | - $PYTHON -m pip install --upgrade "pip<21.0" 38 | - $PYTHON -m pip install -r requirements.txt 39 | - $PYTHON -m pip install cpp-coveralls 40 | # command to run tests 41 | script: 42 | - make -C vm COVERAGE=1 43 | - nosetests -v 44 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022-present, IO Visor Project 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | # All rights reserved. 5 | # 6 | # This source code is licensed in accordance with the terms specified in 7 | # the LICENSE file found in the root directory of this source tree. 8 | # 9 | 10 | include(ExternalProject) 11 | cmake_minimum_required(VERSION 3.16) 12 | project("ubpf") 13 | 14 | include("cmake/platform.cmake") 15 | include("cmake/settings.cmake") 16 | include("cmake/options.cmake") 17 | include("cmake/version.cmake") 18 | 19 | if(UBPF_ENABLE_TESTS) 20 | include("CTest") 21 | endif() 22 | 23 | add_subdirectory("vm") 24 | ExternalProject_Add(Conformance 25 | INSTALL_COMMAND "" 26 | SOURCE_DIR ${CMAKE_SOURCE_DIR}/external/bpf_conformance 27 | BINARY_DIR ${CMAKE_BINARY_DIR}/external/bpf_conformance 28 | EXCLUDE_FROM_ALL true 29 | STEP_TARGETS build) 30 | 31 | if(UBPF_ENABLE_TESTS) 32 | add_subdirectory("custom_tests") 33 | add_subdirectory("ubpf_plugin") 34 | if (NOT UBPF_SKIP_EXTERNAL) 35 | endif() 36 | add_subdirectory("bpf") 37 | add_subdirectory("aarch64_test") 38 | endif() 39 | 40 | if(UBPF_ENABLE_PACKAGE) 41 | include("cmake/packaging.cmake") 42 | endif() 43 | 44 | if (UBPF_ENABLE_LIBFUZZER) 45 | if (PLATFORM_WINDOWS) 46 | # Set compiler flags for libfuzzer 47 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address /fsanitize-coverage=inline-bool-flag /fsanitize-coverage=edge /fsanitize-coverage=trace-cmp /fsanitize-coverage=trace-div") 48 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address /fsanitize-coverage=inline-bool-flag /fsanitize-coverage=edge /fsanitize-coverage=trace-cmp /fsanitize-coverage=trace-div") 49 | endif() 50 | add_subdirectory("libfuzzer") 51 | add_subdirectory("external/ebpf-verifier") 52 | endif() 53 | -------------------------------------------------------------------------------- /aarch64_test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | file(COPY run-interpret.sh DESTINATION ${CMAKE_BINARY_DIR}/bin) 5 | file(COPY run-jit.sh DESTINATION ${CMAKE_BINARY_DIR}/bin) 6 | -------------------------------------------------------------------------------- /aarch64_test/run-interpret.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) Microsoft Corporation 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | # Work around for argument passing. 6 | qemu-aarch64 -L /usr/aarch64-linux-gnu ../bin/ubpf_plugin "$*" --interpret 7 | -------------------------------------------------------------------------------- /aarch64_test/run-jit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (c) Microsoft Corporation 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | # Work around for argument passing. 6 | qemu-aarch64 -L /usr/aarch64-linux-gnu ../bin/ubpf_plugin "$*" --jit 7 | -------------------------------------------------------------------------------- /bin/ubpf-assembler: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | eBPF assembler 4 | 5 | Very simple single-pass assembler. Only exists to assemble testcases 6 | for the interpreter. 7 | """ 8 | 9 | import argparse 10 | import os 11 | import sys 12 | 13 | ROOT_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..") 14 | if os.path.exists(os.path.join(ROOT_DIR, "ubpf")): 15 | # Running from source tree 16 | sys.path.insert(0, ROOT_DIR) 17 | 18 | import ubpf.assembler 19 | 20 | def main(): 21 | parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) 22 | parser.add_argument('input', type=argparse.FileType('r'), default='-', nargs='?') 23 | parser.add_argument('output', type=argparse.FileType('wb'), default='-', nargs='?') 24 | args = parser.parse_args() 25 | 26 | if args.output.name == "" and hasattr(args.output, "buffer"): 27 | # python 3 28 | args.output.buffer.write(ubpf.assembler.assemble(args.input.read())) 29 | else: 30 | args.output.write(ubpf.assembler.assemble(args.input.read())) 31 | 32 | if __name__ == "__main__": 33 | main() 34 | -------------------------------------------------------------------------------- /bin/ubpf-disassembler: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | eBPF disassembler 4 | 5 | Reads the given file or stdin. The input should be raw eBPF 6 | instructions (not an ELF object file). 7 | """ 8 | 9 | import argparse 10 | import os 11 | import sys 12 | 13 | ROOT_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..") 14 | if os.path.exists(os.path.join(ROOT_DIR, "ubpf")): 15 | # Running from source tree 16 | sys.path.insert(0, ROOT_DIR) 17 | 18 | import ubpf.disassembler 19 | 20 | def main(): 21 | parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) 22 | parser.add_argument('input', type=argparse.FileType('rb'), default='-', nargs='?') 23 | parser.add_argument('output', type=argparse.FileType('w'), default='-', nargs='?') 24 | parser.add_argument('--verbose', dest='verbose', action='store_true', default=False) 25 | args = parser.parse_args() 26 | 27 | if args.input.name == "" and hasattr(args.input, "buffer"): 28 | # python 3 29 | input_ = args.input.buffer.read() 30 | else: 31 | input_ = args.input.read() 32 | 33 | print(f"{args.verbose=}") 34 | args.output.write(ubpf.disassembler.disassemble(input_, args.verbose)) 35 | 36 | if __name__ == "__main__": 37 | main() 38 | -------------------------------------------------------------------------------- /bpf/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | function(clang_validator correct clang_path) 5 | execute_process( 6 | COMMAND echo "int main() { return 0;}" 7 | COMMAND ${clang_path} --target=bpf -x c - -c -o /dev/null 8 | ERROR_QUIET OUTPUT_QUIET 9 | RESULT_VARIABLE CLANG_RETURN_CODE 10 | OUTPUT_STRIP_TRAILING_WHITESPACE 11 | ) 12 | 13 | if (CLANG_RETURN_CODE EQUAL 0) 14 | set(${correct} true PARENT_SCOPE) 15 | else() 16 | set(${correct} false PARENT_SCOPE) 17 | endif() 18 | 19 | endfunction() 20 | 21 | find_program(clang_path "clang" VALIDATOR clang_validator NO_CACHE HINTS ${UBPF_ALTERNATE_LLVM_PATH}) 22 | 23 | if(CMAKE_SYSTEM_PROCESSOR STREQUAL aarch64 AND (NOT CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL aarch64)) 24 | set(PREFIX qemu-aarch64 -L /usr/aarch64-linux-gnu) 25 | else() 26 | set(PREFIX) 27 | endif() 28 | 29 | function(build_bpf file_name expected_result optimize) 30 | message(STATUS "Building BPF ${file_name}") 31 | 32 | set(optimize_flags "-O0") 33 | if(${optimize}) 34 | set(optimize_flags "-O2") 35 | endif() 36 | 37 | # An optional parameter sets the name of the section containing main() 38 | # The test program assumes that it is in .text unless otherwise specified. 39 | set(main_section_name_param) 40 | if(${ARGC} EQUAL 4) 41 | list(APPEND main_section_name_param "--main-function" "${ARGV3}") 42 | endif() 43 | 44 | set(bpf_file_name ${file_name}.bpf.c) 45 | set(bpf_file_path ${CMAKE_CURRENT_SOURCE_DIR}/${bpf_file_name}) 46 | set(bpf_obj_file_name ${file_name}.bpf.o) 47 | set(bpf_obj_file_path ${CMAKE_CURRENT_BINARY_DIR}/${bpf_obj_file_name}) 48 | 49 | if (NOT EXISTS ${bpf_file_path}) 50 | message(FATAL_ERROR "BPF file ${bpf_file_path} does not exist") 51 | endif() 52 | 53 | add_custom_command( 54 | OUTPUT ${bpf_obj_file_path} 55 | COMMAND ${clang_path} -g ${optimize_flags} -target bpf -c ${bpf_file_path} -o ${bpf_obj_file_path} 56 | DEPENDS ${bpf_file_path} 57 | COMMENT "Building BPF object ${bpf_obj_file_path}" 58 | ) 59 | 60 | add_custom_target(${file_name}_ELF ALL DEPENDS ${bpf_obj_file_path} SOURCES ${bpf_file_path}) 61 | 62 | add_test(NAME ${file_name}_TEST_INTERPRET COMMAND ${PREFIX} "${CMAKE_BINARY_DIR}/bin/ubpf_test" ${main_section_name_param} "${bpf_obj_file_path}") 63 | set_tests_properties(${file_name}_TEST_INTERPRET PROPERTIES PASS_REGULAR_EXPRESSION ${expected_result}) 64 | add_test(NAME ${file_name}_TEST_JIT COMMAND ${PREFIX} "${CMAKE_BINARY_DIR}/bin/ubpf_test" ${main_section_name_param} "${bpf_obj_file_path}") 65 | set_tests_properties(${file_name}_TEST_JIT PROPERTIES PASS_REGULAR_EXPRESSION ${expected_result}) 66 | endfunction() 67 | 68 | if (NOT ${clang_path} STREQUAL "clang_path-NOTFOUND") 69 | message(WARNING "Clang supports BPF target") 70 | # Note that the regular expressions test whether the final bit of outcome matches 71 | # the expected values. That is because the loader may give warnings about unhandled 72 | # relocation types. 73 | # Also note: We use this odd regex (instead of [\n\r]{1,2}) because CMake does not 74 | # appear to like that syntax. 75 | build_bpf(map "0x0[\n\r]*[\n\r]*$" true) 76 | build_bpf(rel_64_32 "0xe[\n\r]*[\n\r]*$" false "main") 77 | else() 78 | message(WARNING "Clang does not support BPF target, skipping BPF tests") 79 | endif() 80 | -------------------------------------------------------------------------------- /bpf/bpf.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | struct bpf_map; 9 | 10 | static void* (*bpf_map_lookup_elem)(void* map, const void* key) = (void* (*)(void* map, const void* key))6; 11 | static int (*bpf_map_update_elem)(void* map, const void* key, const void* value, unsigned long flags) = 12 | (int (*)(void*, const void*, const void*, unsigned long))7; 13 | static int (*bpf_map_delete_elem)(void* map, const void* key) = (int (*)(void*, const void*))8; 14 | 15 | #define BPF_MAP_TYPE_ARRAY 2 16 | 17 | struct bpf_map_def 18 | { 19 | unsigned int type; 20 | unsigned int key_size; 21 | unsigned int value_size; 22 | unsigned int max_entries; 23 | unsigned int map_flags; 24 | unsigned int inner_map_idx; 25 | unsigned int numa_node; 26 | }; 27 | -------------------------------------------------------------------------------- /bpf/map.bpf.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #include "bpf.h" 5 | 6 | struct bpf_map_def map = { 7 | .type = BPF_MAP_TYPE_ARRAY, 8 | .key_size = sizeof(unsigned int), 9 | .value_size = sizeof(unsigned long), 10 | .max_entries = 10, 11 | }; 12 | 13 | unsigned long 14 | map_test(void* memory, size_t memory_length) 15 | { 16 | unsigned int key = 5; 17 | unsigned long new_value = 0x1234567890ABCDEF; 18 | unsigned long* value = bpf_map_lookup_elem(&map, &key); 19 | if (value == NULL) { 20 | return 1; 21 | } 22 | 23 | if (*value != 0) { 24 | return 2; 25 | } 26 | 27 | if (bpf_map_update_elem(&map, &key, &new_value, 0) != 0) { 28 | return 3; 29 | } 30 | 31 | value = bpf_map_lookup_elem(&map, &key); 32 | if (value == NULL) { 33 | return 4; 34 | } 35 | 36 | if (*value != new_value) { 37 | return 5; 38 | } 39 | 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /bpf/rel_64_32.bpf.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 Big Switch Networks, Inc 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | /* 5 | * Copyright 2023 Will Hawkins 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | static __attribute__((noinline)) __attribute__((section("sec1"))) int 21 | zero() 22 | { 23 | return 5; 24 | } 25 | 26 | static __attribute__((noinline)) __attribute__((section("sec1"))) int 27 | one(int u) 28 | { 29 | return u; 30 | } 31 | 32 | static __attribute__((noinline)) __attribute__((section("sec1"))) int 33 | two() 34 | { 35 | return zero(); 36 | } 37 | 38 | __attribute__((section("__main"))) int 39 | three() 40 | { 41 | return 3; 42 | } 43 | 44 | __attribute__((section("__main"))) int 45 | main() 46 | { 47 | return one(6) + two() + three(); 48 | } 49 | -------------------------------------------------------------------------------- /cmake/arm64.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2022-present, IO Visor Project 3 | # All rights reserved. 4 | # 5 | # This source code is licensed in accordance with the terms specified in 6 | # the LICENSE file found in the root directory of this source tree. 7 | # 8 | 9 | set(CMAKE_SYSTEM_NAME Linux) 10 | set(CMAKE_SYSTEM_PROCESSOR aarch64) 11 | set(CMAKE_SYSTEM_VERSION 1) 12 | set(CMAKE_C_COMPILER /usr/bin/aarch64-linux-gnu-gcc) 13 | set(CMAKE_CXX_COMPILER /usr/bin/aarch64-linux-gnu-g++) 14 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 15 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 16 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) -------------------------------------------------------------------------------- /cmake/options.cmake: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022-present, IO Visor Project 2 | # SPDX-License-Identifier: Apache-2.0 3 | # All rights reserved. 4 | # 5 | # This source code is licensed in accordance with the terms specified in 6 | # the LICENSE file found in the root directory of this source tree. 7 | # 8 | 9 | if(PLATFORM_LINUX OR PLATFORM_MACOS) 10 | option(UBPF_ENABLE_COVERAGE "Set to true to enable coverage flags") 11 | option(UBPF_ENABLE_SANITIZERS "Set to true to enable the address and undefined sanitizers") 12 | option(UBPF_ENABLE_LIBFUZZER "Set to true to enable the libfuzzer") 13 | endif() 14 | 15 | option(UBPF_DISABLE_RETPOLINES "Disable retpoline security on indirect calls and jumps") 16 | option(UBPF_ENABLE_INSTALL "Set to true to enable the install targets") 17 | option(UBPF_ENABLE_TESTS "Set to true to enable tests") 18 | option(UBPF_ENABLE_PACKAGE "Set to true to enable packaging") 19 | option(UBPF_SKIP_EXTERNAL "Set to true to skip external projects") 20 | option(UBPF_INSTALL_GIT_HOOKS "Set to true to install git hooks" ON) 21 | option(BPF_CONFORMANCE_RUNNER "Set to use a custom bpf_conformance runner") 22 | 23 | if(PLATFORM_MACOS) 24 | option(UBPF_ALTERNATE_LLVM_PATH "Set to the path for an alternate (non-Apple) LLVM that supports BPF") 25 | endif() 26 | 27 | # Note that the compile_commands.json file is only exporter when 28 | # using the Ninja or Makefile generator 29 | set(CMAKE_EXPORT_COMPILE_COMMANDS true CACHE BOOL "Set to true to generate the compile_commands.json file (forced on)" FORCE) 30 | -------------------------------------------------------------------------------- /cmake/packaging.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2022-present, IO Visor Project 3 | # All rights reserved. 4 | # 5 | # This source code is licensed in accordance with the terms specified in 6 | # the LICENSE file found in the root directory of this source tree. 7 | # 8 | 9 | if(NOT CPACK_GENERATOR) 10 | message(FATAL_ERROR "ubpf - No generator selected") 11 | endif() 12 | 13 | set(CPACK_PACKAGE_VERSION "${UBPF_VERSION}") 14 | set(CPACK_PACKAGE_DESCRIPTION "Userspace eBPF VM") 15 | set(CPACK_PACKAGE_NAME "${CMAKE_PROJECT_NAME}") 16 | set(CPACK_PACKAGE_VENDOR "IO Visor Project") 17 | set(CPACK_PACKAGE_CONTACT "contact-us@iovisor.org") 18 | set(CPACK_PACKAGE_HOMEPAGE_URL "https://www.iovisor.org") 19 | set(CPACK_PACKAGE_RELOCATABLE ON) 20 | set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE.txt") 21 | set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}") 22 | 23 | if(CPACK_GENERATOR STREQUAL "DEB") 24 | set(CPACK_STRIP_FILES ON) 25 | set(CPACK_DEBIAN_UBPF_PACKAGE_NAME "${CPACK_PACKAGE_NAME}") 26 | set(CPACK_DEBIAN_PACKAGE_RELEASE "${CPACK_PACKAGE_VERSION}") 27 | set(CPACK_DEBIAN_UBPF_FILE_NAME "DEB-DEFAULT") 28 | set(CPACK_DEBIAN_PACKAGE_PRIORITY "extra") 29 | set(CPACK_DEBIAN_PACKAGE_SECTION "default") 30 | set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>=2.31), zlib1g") 31 | set(CPACK_DEBIAN_PACKAGE_HOMEPAGE "${CPACK_PACKAGE_HOMEPAGE_URL}") 32 | 33 | if(CMAKE_BUILD_TYPE STREQUAL "Debug") 34 | set(CPACK_DEB_COMPONENT_INSTALL ON) 35 | set(CPACK_DEBIAN_DEBUGINFO_PACKAGE ON) 36 | endif() 37 | 38 | elseif(CPACK_GENERATOR STREQUAL "RPM") 39 | set(CPACK_STRIP_FILES ON) 40 | set(CPACK_RPM_PACKAGE_RELEASE "${CPACK_PACKAGE_VERSION}") 41 | set(CPACK_RPM_FILE_NAME "RPM-DEFAULT") 42 | set(CPACK_RPM_PACKAGE_DESCRIPTION "${CPACK_PACKAGE_DESCRIPTION}") 43 | set(CPACK_RPM_PACKAGE_GROUP "default") 44 | set(CPACK_RPM_PACKAGE_LICENSE "Apache 2.0") 45 | set(CPACK_RPM_PACKAGE_REQUIRES "glibc >= 2.31, zlib") 46 | 47 | if(CMAKE_BUILD_TYPE STREQUAL "Debug") 48 | set(CPACK_RPM_DEBUGINFO_PACKAGE ON) 49 | set(CPACK_RPM_BUILD_SOURCE_DIRS_PREFIX "/usr/src/debug/ubpf") 50 | set(CPACK_RPM_DEBUGINFO_FILE_NAME "RPM-DEFAULT") 51 | endif() 52 | 53 | elseif(CPACK_GENERATOR STREQUAL "TGZ") 54 | set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY 0) 55 | 56 | if(NOT PLATFORM_WINDOWS) 57 | set(CPACK_SET_DESTDIR ON) 58 | endif() 59 | endif() 60 | 61 | include("CPack") 62 | -------------------------------------------------------------------------------- /cmake/platform.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2022-present, IO Visor Project 3 | # All rights reserved. 4 | # 5 | # This source code is licensed in accordance with the terms specified in 6 | # the LICENSE file found in the root directory of this source tree. 7 | # 8 | 9 | if(CMAKE_SYSTEM_NAME STREQUAL "Windows") 10 | set(PLATFORM_WINDOWS true) 11 | 12 | elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") 13 | set(PLATFORM_MACOS true) 14 | 15 | elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") 16 | set(PLATFORM_LINUX true) 17 | endif() 18 | -------------------------------------------------------------------------------- /cmake/settings.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2022-present, IO Visor Project 3 | # All rights reserved. 4 | # 5 | # This source code is licensed in accordance with the terms specified in 6 | # the LICENSE file found in the root directory of this source tree. 7 | # 8 | 9 | add_library("ubpf_settings" INTERFACE) 10 | 11 | # Only configure our settings target if we are being built directly. 12 | # If we are being used as a submodule, give a chance to the parent 13 | # project to use the settings they want. 14 | if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) 15 | if(PLATFORM_LINUX OR PLATFORM_MACOS) 16 | target_compile_options("ubpf_settings" INTERFACE 17 | -Wall 18 | -Werror 19 | -Iinc 20 | -O2 21 | -Wunused-parameter 22 | -fPIC 23 | ) 24 | 25 | if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR 26 | CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") 27 | 28 | target_compile_options("ubpf_settings" INTERFACE 29 | -O0 30 | -g 31 | ) 32 | endif() 33 | 34 | if(CMAKE_BUILD_TYPE STREQUAL "Debug") 35 | target_compile_definitions("ubpf_settings" INTERFACE 36 | DEBUG 37 | ) 38 | endif() 39 | 40 | if(UBPF_ENABLE_COVERAGE) 41 | target_compile_options("ubpf_settings" INTERFACE 42 | --coverage 43 | -fprofile-arcs 44 | -ftest-coverage 45 | ) 46 | 47 | target_link_options("ubpf_settings" INTERFACE 48 | -fprofile-arcs 49 | ) 50 | endif() 51 | 52 | if(UBPF_ENABLE_SANITIZERS) 53 | set(sanitizer_flags 54 | -fno-omit-frame-pointer 55 | -fsanitize=undefined,address 56 | ) 57 | 58 | target_compile_options("ubpf_settings" INTERFACE 59 | ${sanitizer_flags} 60 | ) 61 | 62 | target_link_options("ubpf_settings" INTERFACE 63 | ${sanitizer_flags} 64 | ) 65 | endif() 66 | 67 | if(UBPF_ENABLE_LIBFUZZER) 68 | set(fuzzer_flags 69 | -g 70 | -O0 71 | -fsanitize=fuzzer 72 | -fsanitize=address 73 | -fsanitize-coverage=edge,indirect-calls,trace-cmp,trace-div,trace-gep 74 | ) 75 | 76 | # Check if compiler is clang and emit error if not 77 | if(NOT CMAKE_C_COMPILER_ID STREQUAL "Clang") 78 | message(FATAL_ERROR "LibFuzzer is only supported with Clang") 79 | endif() 80 | 81 | target_compile_options("ubpf_settings" INTERFACE 82 | ${fuzzer_flags} 83 | ) 84 | 85 | target_link_options("ubpf_settings" INTERFACE 86 | ${fuzzer_flags} 87 | ) 88 | endif() 89 | elseif(PLATFORM_WINDOWS) 90 | set(CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION "8.1") 91 | 92 | target_compile_options("ubpf_settings" INTERFACE 93 | /W4 94 | ) 95 | 96 | target_compile_definitions("ubpf_settings" INTERFACE 97 | UNICODE 98 | _UNICODE 99 | 100 | $<$:DEBUG> 101 | $<$:NDEBUG> 102 | $<$:NDEBUG> 103 | ) 104 | 105 | else() 106 | message(WARNING "ubpf - Unsupported platform") 107 | endif() 108 | endif() 109 | 110 | if(UBPF_ENABLE_INSTALL) 111 | install( 112 | TARGETS 113 | "ubpf_settings" 114 | 115 | EXPORT 116 | "ubpf" 117 | ) 118 | endif() 119 | 120 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 121 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 122 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 123 | -------------------------------------------------------------------------------- /cmake/version.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2022-present, IO Visor Project 3 | # All rights reserved. 4 | # 5 | # This source code is licensed in accordance with the terms specified in 6 | # the LICENSE file found in the root directory of this source tree. 7 | # 8 | 9 | set(UBPF_VERSION "1.0.0") 10 | -------------------------------------------------------------------------------- /compat/macOS/elfdefinitions.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2010,2021 Joseph Koshy 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | * 26 | * $Id: elfdefinitions.h 3942 2021-04-05 12:16:03Z jkoshy $ 27 | */ 28 | 29 | #ifndef _ELFDEFINITIONS_H_ 30 | #define _ELFDEFINITIONS_H_ 31 | 32 | #include 33 | 34 | #endif /* _ELFDEFINITIONS_H_ */ 35 | -------------------------------------------------------------------------------- /custom_tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | set(CMAKE_CXX_STANDARD 20) 5 | 6 | file(GLOB test_descr_files ${CMAKE_SOURCE_DIR}/custom_tests/descrs/*.md) 7 | 8 | add_library(ubpf_custom_test_support srcs/ubpf_custom_test_support.cc) 9 | 10 | target_link_libraries( 11 | ubpf_custom_test_support 12 | ubpf 13 | ubpf_settings 14 | ) 15 | 16 | target_include_directories(ubpf_custom_test_support PUBLIC ".srcs/") 17 | target_include_directories(ubpf_custom_test_support PRIVATE 18 | "${CMAKE_SOURCE_DIR}/vm" 19 | "${CMAKE_BINARY_DIR}/vm" 20 | "${CMAKE_SOURCE_DIR}/vm/inc" 21 | "${CMAKE_BINARY_DIR}/vm/inc" 22 | ) 23 | 24 | set(QEMU_RUNNER "") 25 | if(CMAKE_SYSTEM_PROCESSOR STREQUAL aarch64 AND (NOT CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL aarch64)) 26 | set(QEMU_RUNNER qemu-aarch64 -L /usr/aarch64-linux-gnu) 27 | endif() 28 | 29 | foreach(test_file ${test_descr_files}) 30 | get_filename_component(test_name ${test_file} NAME_WE) 31 | set(test_source_path "${CMAKE_SOURCE_DIR}/custom_tests/srcs/${test_name}.cc") 32 | 33 | add_executable( 34 | ${test_name} 35 | ${test_source_path} 36 | ) 37 | target_include_directories(${test_name} PRIVATE 38 | "${CMAKE_SOURCE_DIR}/vm" 39 | "${CMAKE_BINARY_DIR}/vm" 40 | "${CMAKE_SOURCE_DIR}/vm/inc" 41 | "${CMAKE_BINARY_DIR}/vm/inc" 42 | ) 43 | target_link_libraries( 44 | ${test_name} 45 | ubpf 46 | ubpf_custom_test_support 47 | ubpf_settings 48 | ) 49 | set(potential_input_file ${CMAKE_SOURCE_DIR}/custom_tests/data/${test_name}.input) 50 | if (EXISTS ${potential_input_file}) 51 | list(JOIN QEMU_RUNNER " " QEMU_RUNNER_STR) 52 | if(PLATFORM_WINDOWS) 53 | set(test_exe ${CMAKE_BINARY_DIR}\\bin\\$<$:Debug>$<$:RelWithDebInfo>\\${test_name}.exe) 54 | # Replace forward slashes with backslashes in the path to the input file 55 | string(REPLACE "/" "\\" potential_input_file ${potential_input_file}) 56 | # Replace forward slashes with backslashes in the path to the test executable 57 | string(REPLACE "/" "\\" test_exe ${test_exe}) 58 | add_test( 59 | NAME ${test_name}-Custom 60 | COMMAND ${test_exe} --program ${potential_input_file} 61 | ) 62 | else() 63 | add_test( 64 | NAME ${test_name}-Custom 65 | COMMAND sh -c "cat ${potential_input_file} | ${QEMU_RUNNER_STR} $" 66 | ) 67 | endif() 68 | else() 69 | add_test( 70 | NAME ${test_name}-Custom 71 | COMMAND ${QEMU_RUNNER} $ 72 | ) 73 | endif() 74 | endforeach() 75 | -------------------------------------------------------------------------------- /custom_tests/README.md: -------------------------------------------------------------------------------- 1 | ## Writing a uBPF Custom Tests 2 | 3 | Custom tests are enabled by creating two (2) or three (3) different files in the `custom_tests` directory. 4 | 5 | ### Files Of a uBPF Custom Test 6 | 7 | #### Description Files 8 | 9 | The first file to create is the Description File. The Description File is a file with a `.md` extension that resides in the `descrs` directory. The purpose of this file is to identify the name of the test (everything before the `.md` extension) and provide a place to document the purpose of the test. 10 | 11 | #### Source Files 12 | 13 | The second file to create is the Source File. The Source file should reside in the `srcs` directory and have a name that matches its Description File (with the `.cc` extension rather than the `.md` extension). 14 | 15 | #### Input Files 16 | 17 | The final file is optional. The Input File resides in the `data` directory and should have the same name as the other two (2) files but with an `.input` extension rather than `.cc` or `.md` for the Source and Description File respectively. If present, the contents of this file will be given to the executed custom test over standard input. 18 | 19 | ### Building 20 | 21 | The Source Files for a custom test are compiled using C++20 and are saved as an executable named according to the name of the test in the CMake build directory. 22 | 23 | ### Return Values 24 | 25 | All successful tests should return `0`. All failing tests should return something other than `0`. 26 | 27 | ### Supporting Libraries 28 | 29 | To reduce the boilerplate needed to write custom tests, there is a custom test library with several helpful functions. These functions are documented in the library's header file (`custom_tests/srcs/ubpf_custom_test_support.h`). 30 | 31 | ### Putting It Together 32 | 33 | After describing the test's purpose in a Markdown syntax in a file named, say, `test_example.md` and stored in the `descrs` directory, you can write the test's Source Code (in C++20) and give it the name `test_example.cc` in the `srcs` directory. If the test needs input, you can save that input in the tests Input File (`test_input.input`) in the `data` directory. 34 | 35 | Because all the files are present, this test will be run when the CTest target is invoked. Because there the optional `test_input.input` file is present, the contents of that file will be given to the executable via standard input. -------------------------------------------------------------------------------- /custom_tests/data/ubpf_test_atomic_validate.input: -------------------------------------------------------------------------------- 1 | b4 00 00 00 00 00 00 00 db 01 00 00 42 00 00 00 95 00 00 00 00 00 00 00 2 | -------------------------------------------------------------------------------- /custom_tests/data/ubpf_test_custom_local_function_stack_size.input: -------------------------------------------------------------------------------- 1 | 72 0a fc ff 01 00 00 00 72 0a fd ff 02 00 00 00 72 0a fe ff 03 00 00 00 72 0a ff ff 04 00 00 00 85 10 00 00 02 00 00 00 b7 00 00 00 00 00 00 00 95 00 00 00 00 00 00 00 72 0a fc ff 05 00 00 00 72 0a fd ff 06 00 00 00 72 0a fe ff 07 00 00 00 72 0a ff ff 08 00 00 00 95 00 00 00 00 00 00 00 2 | -------------------------------------------------------------------------------- /custom_tests/data/ubpf_test_custom_local_function_stack_size_zero.input: -------------------------------------------------------------------------------- 1 | 72 0a fc ff 01 00 00 00 72 0a fd ff 02 00 00 00 72 0a fe ff 03 00 00 00 72 0a ff ff 04 00 00 00 85 10 00 00 02 00 00 00 b7 00 00 00 00 00 00 00 95 00 00 00 00 00 00 00 72 0a fc ff 05 00 00 00 72 0a fd ff 06 00 00 00 72 0a fe ff 07 00 00 00 72 0a ff ff 08 00 00 00 95 00 00 00 00 00 00 00 2 | -------------------------------------------------------------------------------- /custom_tests/data/ubpf_test_debug_function.input: -------------------------------------------------------------------------------- 1 | 85 00 00 00 01 00 00 00 95 00 00 00 00 00 00 00 2 | -------------------------------------------------------------------------------- /custom_tests/data/ubpf_test_default_dispatcher_helper_context.input: -------------------------------------------------------------------------------- 1 | 85 00 00 00 01 00 00 00 95 00 00 00 00 00 00 00 2 | -------------------------------------------------------------------------------- /custom_tests/data/ubpf_test_default_local_function_stack_size.input: -------------------------------------------------------------------------------- 1 | 72 0a ff ff 01 00 00 00 72 0a fe ff 02 00 00 00 72 0a fd ff 03 00 00 00 72 0a fc ff 04 00 00 00 85 10 00 00 02 00 00 00 b7 00 00 00 00 00 00 00 95 00 00 00 00 00 00 00 72 0a ff ff 11 00 00 00 72 0a fe ff 12 00 00 00 72 0a fd ff 13 00 00 00 72 0a fc ff 14 00 00 00 95 00 00 00 00 00 00 00 -------------------------------------------------------------------------------- /custom_tests/data/ubpf_test_external_dispatcher_context_overwrite.input: -------------------------------------------------------------------------------- 1 | b7 01 00 00 01 02 03 04 85 00 00 00 01 00 00 00 95 00 00 00 00 00 00 00 2 | -------------------------------------------------------------------------------- /custom_tests/data/ubpf_test_external_dispatcher_simple_context.input: -------------------------------------------------------------------------------- 1 | 85 00 00 00 01 00 00 00 95 00 00 00 00 00 00 00 2 | -------------------------------------------------------------------------------- /custom_tests/data/ubpf_test_frame_pointer.input: -------------------------------------------------------------------------------- 1 | b7 06 00 00 0a 00 00 00 b7 07 00 00 0a 00 00 00 b7 08 00 00 0a 00 00 00 b7 09 00 00 0a 00 00 00 b7 01 00 00 05 00 00 00 7b 1a f8 ff 00 00 00 00 85 10 00 00 02 00 00 00 79 a0 f8 ff 00 00 00 00 95 00 00 00 00 00 00 00 b7 01 00 00 37 00 00 00 7b 1a f8 ff 00 00 00 00 95 00 00 00 00 00 00 00 2 | -------------------------------------------------------------------------------- /custom_tests/data/ubpf_test_jit_unexpected_instruction.input: -------------------------------------------------------------------------------- 1 | 8f 00 00 00 01 00 00 00 -------------------------------------------------------------------------------- /custom_tests/data/ubpf_test_update_dispatcher.input: -------------------------------------------------------------------------------- 1 | 85 00 00 00 01 00 00 00 95 00 00 00 00 00 00 00 2 | -------------------------------------------------------------------------------- /custom_tests/data/ubpf_test_update_helpers.input: -------------------------------------------------------------------------------- 1 | 85 00 00 00 01 00 00 00 95 00 00 00 00 00 00 00 2 | -------------------------------------------------------------------------------- /custom_tests/descrs/ubpf_test_atomic_validate.md: -------------------------------------------------------------------------------- 1 | ## Test Description 2 | 3 | This test verifies that the check for to validate an instruction properly handles the case where an atomic operation's immediate field does not contain a valid operation (according to Table 11 in the [spec](https://www.ietf.org/archive/id/draft-thaler-bpf-isa-00.html). -------------------------------------------------------------------------------- /custom_tests/descrs/ubpf_test_custom_local_function_stack_size.md: -------------------------------------------------------------------------------- 1 | ## Test Description 2 | 3 | This custom test guarantees that the eBPF program's manipulation of its stack has the intended effect. The eBPF program is JIT'd under the assumption that each of the functions require 16 bytes of space. The test guarantees that by returning `16` from `stack_usage_calculator` which is registered as the callback that will determine the stack usage of a local function. 4 | 5 | ### eBPF Program Source 6 | 7 | ``` 8 | stb [%r10-4], 0x01 9 | stb [%r10-3], 0x02 10 | stb [%r10-2], 0x03 11 | stb [%r10-1], 0x04 12 | call local inner 13 | mov %r0, 0 14 | exit 15 | inner: 16 | stb [%r10-4], 0x05 17 | stb [%r10-3], 0x06 18 | stb [%r10-2], 0x07 19 | stb [%r10-1], 0x08 20 | exit 21 | ``` 22 | 23 | ### Expected Behavior 24 | 25 | Given the size of the stack usage for each function (see above), the contents of the memory at the end of the program will be: 26 | 27 | ``` 28 | 0x00: 0x00 29 | 0x01: 0x00 30 | 0x02: 0x00 31 | 0x03: 0x00 32 | 0x04: 0x00 33 | 0x05: 0x00 34 | 0x06: 0x00 35 | 0x07: 0x00 36 | 0x08: 0x00 37 | 0x09: 0x00 38 | 0x0a: 0x00 39 | 0x0b: 0x05 40 | 0x0c: 0x06 41 | 0x0d: 0x07 42 | 0x0e: 0x08 43 | 0x0f: 0x00 44 | 0x10: 0x00 45 | 0x11: 0x00 46 | 0x12: 0x00 47 | 0x13: 0x00 48 | 0x14: 0x00 49 | 0x15: 0x00 50 | 0x16: 0x00 51 | 0x17: 0x00 52 | 0x18: 0x00 53 | 0x19: 0x00 54 | 0x1a: 0x00 55 | 0x1b: 0x01 56 | 0x1c: 0x02 57 | 0x1d: 0x03 58 | 0x1e: 0x04 59 | ``` 60 | -------------------------------------------------------------------------------- /custom_tests/descrs/ubpf_test_custom_local_function_stack_size_unaligned.md: -------------------------------------------------------------------------------- 1 | ## Test Description 2 | 3 | This custom test guarantees that the uBPF runtime properly detects a user who sets a non-16-byte-aligned custom size for a local function's stack usage. 4 | 5 | ### eBPF Program Source 6 | 7 | N/A 8 | 9 | ### Expected Behavior 10 | 11 | An error reporting that a stack size for local functions of 17 bytes has the improper alignment. -------------------------------------------------------------------------------- /custom_tests/descrs/ubpf_test_custom_local_function_stack_size_zero.md: -------------------------------------------------------------------------------- 1 | ## Test Description 2 | 3 | This custom test guarantees that the uBPF runtime can properly handle custom stack sizes for local functions of 0 bytes. The test works by executing an eBPF program with a local function that writes to its stack. If uBPF respects a 0-size custom stack size, then the write to the stack in the local function will 4 | end up at the top of the program's stack. 5 | 6 | ### eBPF Program Source 7 | 8 | ``` 9 | stb [%r10-4], 0x01 10 | stb [%r10-3], 0x02 11 | stb [%r10-2], 0x03 12 | stb [%r10-1], 0x04 13 | call local inner 14 | mov %r0, 0 15 | exit 16 | inner: 17 | stb [%r10-4], 0x05 18 | stb [%r10-3], 0x06 19 | stb [%r10-2], 0x07 20 | stb [%r10-1], 0x08 21 | exit 22 | ``` 23 | 24 | ### Expected Behavior 25 | 26 | Given the size of the stack usage for each function (see above), the contents of the memory at the end of the program will be: 27 | 28 | ``` 29 | 0x00: 0x00 30 | 0x01: 0x00 31 | 0x02: 0x00 32 | 0x03: 0x00 33 | 0x04: 0x00 34 | 0x05: 0x00 35 | 0x06: 0x00 36 | 0x07: 0x00 37 | 0x08: 0x00 38 | 0x09: 0x00 39 | 0x0a: 0x00 40 | 0x0b: 0x00 41 | 0x0c: 0x00 42 | 0x0d: 0x00 43 | 0x0e: 0x00 44 | 0x0f: 0x00 45 | 0x10: 0x00 46 | 0x11: 0x00 47 | 0x12: 0x00 48 | 0x13: 0x00 49 | 0x14: 0x00 50 | 0x15: 0x00 51 | 0x16: 0x00 52 | 0x17: 0x00 53 | 0x18: 0x00 54 | 0x19: 0x00 55 | 0x1a: 0x00 56 | 0x1b: 0x05 57 | 0x1c: 0x06 58 | 0x1d: 0x07 59 | 0x1e: 0x08 60 | ``` 61 | -------------------------------------------------------------------------------- /custom_tests/descrs/ubpf_test_debug_function.md: -------------------------------------------------------------------------------- 1 | ## Test Description 2 | 3 | This test verifies that a debug call-out can be registered to inspect the state of the vm prior to each instruction. 4 | -------------------------------------------------------------------------------- /custom_tests/descrs/ubpf_test_default_dispatcher_helper_context.md: -------------------------------------------------------------------------------- 1 | ##Test Description 2 | 3 | This custom test program tests whether JIT'd and interpreted eBPF programs properly pass the context to external 4 | helper functions. 5 | -------------------------------------------------------------------------------- /custom_tests/descrs/ubpf_test_default_local_function_stack_size.md: -------------------------------------------------------------------------------- 1 | ## Test Description 2 | 3 | This custom test guarantees that the eBPF program's manipulation of its stack has the intended effect. The eBPF program is JIT'd/interpreted under the assumption that each of the functions will use the default amount of stack space (256 bytes). This test will guarantee that with an eBPF program that writes at specific spots in the program's stack and then checks whether those writes put data in the proper spot on the program's stack. 4 | 5 | ### eBPF Program Source 6 | 7 | ``` 8 | stb [%r10-1], 0x1 9 | stb [%r10-2], 0x2 10 | stb [%r10-3], 0x3 11 | stb [%r10-4], 0x4 12 | call local func1 13 | mov %r0, 0x0 14 | exit 15 | 16 | func1: 17 | stb [%r10-1], 0x11 18 | stb [%r10-2], 0x12 19 | stb [%r10-3], 0x13 20 | stb [%r10-4], 0x14 21 | exit 22 | ``` 23 | 24 | ### Expected Behavior 25 | 26 | Given the size of the stack usage for each function (see above), the contents of the memory at the end of the program will be: 27 | 28 | ``` 29 | 0x1efc: 0x14 30 | 0x1efd: 0x13 31 | 0x1efe: 0x12 32 | 0x1eff: 0x11 33 | ... 34 | 0x1ffa: 0x00 35 | 0x1ffb: 0x00 36 | 0x1ffc: 0x04 37 | 0x1ffd: 0x03 38 | 0x1ffe: 0x02 39 | 0x1fff: 0x01 40 | ``` 41 | -------------------------------------------------------------------------------- /custom_tests/descrs/ubpf_test_external_dispatcher_context_overwrite.md: -------------------------------------------------------------------------------- 1 | ## Test Description 2 | 3 | This custom test program tests whether JIT'd eBPF programs properly pass the original context 4 | to external helper dispatcher even when (eBPF) register r0 has been modified. The original 5 | context to the eBPF program is passed in (eBPF) register r0. Subsequent changes to that 6 | register by the eBPF program should *not* affect that context (which is given to the 7 | helper function external dispatcher). -------------------------------------------------------------------------------- /custom_tests/descrs/ubpf_test_external_dispatcher_simple_context.md: -------------------------------------------------------------------------------- 1 | ## Test Description 2 | 3 | This custom test program tests whether JIT'd eBPF programs properly pass the context 4 | to external helper dispatcher. 5 | -------------------------------------------------------------------------------- /custom_tests/descrs/ubpf_test_frame_pointer.md: -------------------------------------------------------------------------------- 1 | ## Test Description 2 | 3 | This custom test program tests whether it is possible to update the external helper 4 | functions for an eBPF program that has already been JIT'd. 5 | -------------------------------------------------------------------------------- /custom_tests/descrs/ubpf_test_jit_buffer_too_small.md: -------------------------------------------------------------------------------- 1 | ## Test Description 2 | 3 | This custom test program tests whether compilation fails (with the proper error message) when 4 | the user gives a buffer that is too small to accommodate the size of the JIT'd code. 5 | -------------------------------------------------------------------------------- /custom_tests/descrs/ubpf_test_jit_unexpected_instruction.md: -------------------------------------------------------------------------------- 1 | ## Test Description 2 | 3 | This custom test program tests that an eBPF program fails to load (with the proper error) in 4 | the presence of a program with an invalid instruction opcode. 5 | -------------------------------------------------------------------------------- /custom_tests/descrs/ubpf_test_update_dispatcher.md: -------------------------------------------------------------------------------- 1 | ## Test Description 2 | 3 | This custom test program tests whether it is possible to update the external helper dispatcher 4 | after an eBPF program has been compiled. 5 | -------------------------------------------------------------------------------- /custom_tests/descrs/ubpf_test_update_helpers.md: -------------------------------------------------------------------------------- 1 | ## Test Description 2 | 3 | This custom test program tests whether it is possible to update the external helper 4 | functions for an eBPF program that has already been JIT'd. 5 | -------------------------------------------------------------------------------- /custom_tests/srcs/ubpf_custom_test_support.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | extern "C" 15 | { 16 | #include "ebpf.h" 17 | #include "ubpf.h" 18 | } 19 | 20 | 21 | #include "ubpf_custom_test_support.h" 22 | 23 | std::vector 24 | base16_decode(const std::string &input) 25 | { 26 | std::vector output; 27 | std::stringstream ss(input); 28 | std::string value; 29 | output.reserve(input.size() / 3); 30 | while (std::getline(ss, value, ' ')) 31 | { 32 | try 33 | { 34 | output.push_back(static_cast(std::stoi(value, nullptr, 16))); 35 | } 36 | catch (...) 37 | { 38 | // Ignore invalid values. 39 | } 40 | } 41 | return output; 42 | } 43 | 44 | std::vector 45 | bytes_to_ebpf_inst(std::vector bytes) 46 | { 47 | std::vector instructions(bytes.size() / sizeof(ebpf_inst)); 48 | memcpy(instructions.data(), bytes.data(), bytes.size()); 49 | return instructions; 50 | } 51 | 52 | 53 | bool ubpf_setup_custom_test(ubpf_vm_up &vm, 54 | const std::string program_string, 55 | std::optional fixup_f, 56 | ubpf_jit_fn &jit_fn, 57 | std::string &error) 58 | { 59 | jit_fn = nullptr; 60 | std::vector program = bytes_to_ebpf_inst(base16_decode(program_string)); 61 | char *error_s{nullptr}; 62 | 63 | if (vm == nullptr) 64 | { 65 | error = "VM not provided"; 66 | return false; 67 | } 68 | 69 | if (ubpf_set_unwind_function_index(vm.get(), 5) != 0) 70 | { 71 | error = "Failed to set unwind function index"; 72 | return false; 73 | } 74 | 75 | if (fixup_f.has_value()) 76 | { 77 | if (!(fixup_f.value())(vm, error)) { 78 | return false; 79 | } 80 | } 81 | 82 | if (ubpf_load(vm.get(), program.data(), static_cast(program.size() * sizeof(ebpf_inst)), &error_s) != 0) 83 | { 84 | error = "Failed to load program: " + std::string{error_s}; 85 | free(error_s); 86 | return false; 87 | } 88 | 89 | jit_fn = ubpf_compile(vm.get(), &error_s); 90 | if (jit_fn == nullptr) 91 | { 92 | error = "Failed to compile: " + std::string{error_s}; 93 | free(error_s); 94 | return false; 95 | } 96 | 97 | assert(error_s == nullptr); 98 | free(error_s); 99 | return true; 100 | } 101 | 102 | bool get_program_string(int argc, char **argv, std::string &program_string, std::string &error) 103 | { 104 | std::vector args(argv, argv + argc); 105 | 106 | if (args.size() == 1) 107 | { 108 | std::string line; 109 | while (std::getline(std::cin, line)) 110 | { 111 | program_string += line; 112 | } 113 | } 114 | else if (args.size() == 3 && args[1] == "--program") 115 | { 116 | std::ifstream file(args[2]); 117 | if (file.is_open()) 118 | { 119 | std::string line; 120 | while (std::getline(file, line)) 121 | { 122 | program_string += line; 123 | } 124 | file.close(); 125 | } 126 | else 127 | { 128 | error = "Failed to open file: " + args[2]; 129 | return false; 130 | } 131 | } 132 | else 133 | { 134 | error = "Usage: " + args[0] + " [program]"; 135 | return false; 136 | } 137 | 138 | return true; 139 | } 140 | -------------------------------------------------------------------------------- /custom_tests/srcs/ubpf_custom_test_support.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | extern "C" 12 | { 13 | #include "ebpf.h" 14 | #include "ubpf.h" 15 | } 16 | 17 | #define UNREFERENCED_PARAMETER (void) 18 | 19 | /** 20 | * @brief Read in a string of hex bytes and return a vector of bytes. 21 | * 22 | * @param[in] input String containing hex bytes. 23 | * @return Vector of bytes. 24 | */ 25 | std::vector 26 | base16_decode(const std::string &input); 27 | 28 | 29 | /** 30 | * @brief Convert a vector of bytes to a vector of ebpf_inst. 31 | * 32 | * @param[in] bytes Vector of bytes. 33 | * @return Vector of ebpf_inst. 34 | */ 35 | std::vector 36 | bytes_to_ebpf_inst(std::vector bytes); 37 | 38 | 39 | using ubpf_vm_up = std::unique_ptr; 40 | using custom_test_fixup_cb = std::function; 41 | 42 | 43 | /** 44 | * @brief Do the common necessary work to setup a custom test. 45 | * 46 | * @param[in] vm The VM for which to prepare the test. 47 | * @param[in] program_string A string of raw bytes that make up the eBPF program to execute under this test. 48 | * @param[in] fixup_f A function that will be invoked after the program is loaded and before it is compiled. 49 | * @param[out] jit_fn A function that can be invoked to run the jit'd program. 50 | * @param[out] error A string containing the error message (if any) generated during custom test configuration. 51 | * @return True or false depending on whether setting up the custom test succeeded. 52 | */ 53 | bool ubpf_setup_custom_test(ubpf_vm_up &vm, 54 | const std::string program_string, 55 | std::optional fixup_f, 56 | ubpf_jit_fn &jit_fn, 57 | std::string &error); 58 | 59 | /** 60 | * @brief Get the program string object from the command line arguments or stdin. 61 | * 62 | * @param[in] argc The number of command line arguments. 63 | * @param[in] argv The command line arguments. 64 | * @param[out] program_string The program string to return. 65 | * @param[out] error The error message to return if there is a problem. 66 | * @return true If the program string was successfully obtained. 67 | * @return false If there was a problem obtaining the program string. 68 | */ 69 | bool get_program_string(int argc, char **argv, std::string &program_string, std::string &error); 70 | -------------------------------------------------------------------------------- /custom_tests/srcs/ubpf_test_atomic_validate.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) Will Hawkins 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | extern "C" 11 | { 12 | #include "ubpf.h" 13 | } 14 | 15 | #include "ubpf_custom_test_support.h" 16 | 17 | int 18 | main(int argc, char** argv) 19 | { 20 | std::string program_string{}; 21 | std::string error{}; 22 | ubpf_jit_fn jit_fn; 23 | 24 | if (!get_program_string(argc, argv, program_string, error)) { 25 | std::cerr << error << std::endl; 26 | return 1; 27 | } 28 | 29 | uint64_t memory_expected{0x123456789}; 30 | uint64_t memory{0x123456789}; 31 | 32 | std::unique_ptr vm(ubpf_create(), ubpf_destroy); 33 | if (!ubpf_setup_custom_test( 34 | vm, program_string, [](ubpf_vm_up&, std::string&) { return true; }, jit_fn, error)) { 35 | if (error == "Failed to load program: Invalid immediate value 66 for opcode DB.") { 36 | return 0; 37 | } 38 | 39 | return 1; 40 | } 41 | 42 | return 1; 43 | 44 | uint64_t bpf_return_value; 45 | if (ubpf_exec(vm.get(), &memory, sizeof(memory), &bpf_return_value)) { 46 | std::cerr << "Problem executing program" << std::endl; 47 | return 1; 48 | } 49 | 50 | return !(memory == memory_expected); 51 | } 52 | -------------------------------------------------------------------------------- /custom_tests/srcs/ubpf_test_custom_local_function_stack_size.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) Will Hawkins 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | extern "C" 12 | { 13 | #include "ubpf.h" 14 | } 15 | 16 | #include "ubpf_custom_test_support.h" 17 | 18 | int 19 | stack_usage_calculator(const struct ubpf_vm* vm, uint16_t pc, void* cookie) 20 | { 21 | UNREFERENCED_PARAMETER(vm); 22 | UNREFERENCED_PARAMETER(pc); 23 | UNREFERENCED_PARAMETER(cookie); 24 | return 16; 25 | } 26 | 27 | int 28 | main(int argc, char** argv) 29 | { 30 | std::string program_string{}; 31 | std::string error{}; 32 | ubpf_jit_fn jit_fn; 33 | uint64_t jit_result{}; 34 | uint64_t interp_result{}; 35 | 36 | if (!get_program_string(argc, argv, program_string, error)) { 37 | std::cerr << error << std::endl; 38 | return 1; 39 | } 40 | 41 | const size_t stack_size{32}; 42 | uint8_t expected_result[] = { 43 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 44 | }; 45 | 46 | bool success = true; 47 | 48 | std::unique_ptr vm(ubpf_create(), ubpf_destroy); 49 | if (!ubpf_setup_custom_test( 50 | vm, 51 | program_string, 52 | [](ubpf_vm_up& vm, std::string& error) { 53 | if (ubpf_register_stack_usage_calculator(vm.get(), stack_usage_calculator, nullptr) < 0) { 54 | error = "Failed to register stack usage calculator."; 55 | return false; 56 | } 57 | return true; 58 | }, 59 | jit_fn, 60 | error)) { 61 | std::cerr << "Problem setting up custom test: " << error << std::endl; 62 | return 1; 63 | } 64 | 65 | char* ex_jit_compile_error = nullptr; 66 | auto jit_ex_fn = ubpf_compile_ex(vm.get(), &ex_jit_compile_error, ExtendedJitMode); 67 | uint8_t external_stack[stack_size] = { 68 | 0, 69 | }; 70 | jit_result = jit_ex_fn(nullptr, 0, external_stack, stack_size); 71 | 72 | if (jit_result) { 73 | std::cerr << "Execution of the JIT'd program gave a non-0 result.\n"; 74 | return 1; 75 | } 76 | 77 | for (size_t i = 0; i < stack_size; i++) { 78 | if (external_stack[i] != expected_result[i]) { 79 | std::cerr << "Byte 0x" << std::hex << i << " different between expected (0x" << (uint32_t)expected_result[i] 80 | << ") and actual (0x" << (uint32_t)external_stack[i] << ")\n"; 81 | success = false; 82 | } 83 | } 84 | 85 | if (!success) { 86 | return !success; 87 | } 88 | 89 | std::memset(external_stack, 0x0, sizeof(external_stack)); 90 | int interp_success{ubpf_exec_ex(vm.get(), nullptr, 0, &interp_result, external_stack, stack_size)}; 91 | 92 | if (interp_success < 0) { 93 | std::cerr << "There was an error interpreting the program: " << success << "\n"; 94 | return 1; 95 | } 96 | 97 | if (interp_result) { 98 | std::cerr << "Execution of the interpreted program gave a non-0 result.\n"; 99 | return 1; 100 | } 101 | 102 | for (size_t i = 0; i < stack_size; i++) { 103 | if (external_stack[i] != expected_result[i]) { 104 | std::cerr << "Byte 0x" << std::hex << i << " different between expected (0x" << (uint32_t)expected_result[i] 105 | << ") and actual (0x" << (uint32_t)external_stack[i] << ")\n"; 106 | success = false; 107 | } 108 | } 109 | 110 | if (!success) { 111 | return !success; 112 | } 113 | 114 | return 0; 115 | 116 | } 117 | -------------------------------------------------------------------------------- /custom_tests/srcs/ubpf_test_custom_local_function_stack_size_unaligned.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) Will Hawkins 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #include "ubpf_int.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | extern "C" 12 | { 13 | #include "ubpf.h" 14 | } 15 | 16 | #include "ubpf_custom_test_support.h" 17 | 18 | int 19 | stack_usage_calculator(const struct ubpf_vm* vm, uint16_t pc, void* cookie) 20 | { 21 | UNREFERENCED_PARAMETER(vm); 22 | UNREFERENCED_PARAMETER(pc); 23 | UNREFERENCED_PARAMETER(cookie); 24 | return 17; 25 | } 26 | 27 | int 28 | main(int argc, char** argv) 29 | { 30 | UNUSED_PARAMETER(argc); 31 | UNUSED_PARAMETER(argv); 32 | 33 | std::string error{}; 34 | ubpf_jit_fn jit_fn; 35 | 36 | std::unique_ptr vm(ubpf_create(), ubpf_destroy); 37 | if (!ubpf_setup_custom_test( 38 | vm, 39 | "", 40 | [](ubpf_vm_up& vm, std::string& error) { 41 | if (ubpf_register_stack_usage_calculator(vm.get(), stack_usage_calculator, nullptr) < 0) { 42 | error = "Failed to register stack usage calculator."; 43 | return false; 44 | } 45 | return true; 46 | }, 47 | jit_fn, 48 | error)) { 49 | if (error != "Failed to load program: local function " 50 | "(at PC 0) has improperly sized stack use (17)") { 51 | std::cerr << "Did not get the expected error regarding unaligned stack size for local function: " << error 52 | << "\n"; 53 | return 1; 54 | } 55 | } 56 | 57 | return 0; 58 | } 59 | -------------------------------------------------------------------------------- /custom_tests/srcs/ubpf_test_custom_local_function_stack_size_zero.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) Will Hawkins 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | extern "C" 12 | { 13 | #include "ubpf.h" 14 | } 15 | 16 | #include "ubpf_custom_test_support.h" 17 | 18 | int 19 | stack_usage_calculator(const struct ubpf_vm* vm, uint16_t pc, void* cookie) 20 | { 21 | UNREFERENCED_PARAMETER(vm); 22 | UNREFERENCED_PARAMETER(pc); 23 | UNREFERENCED_PARAMETER(cookie); 24 | return 0; 25 | } 26 | 27 | int 28 | main(int argc, char** argv) 29 | { 30 | std::string program_string{}; 31 | std::string error{}; 32 | ubpf_jit_fn jit_fn; 33 | uint64_t jit_result{}; 34 | uint64_t interp_result{}; 35 | 36 | if (!get_program_string(argc, argv, program_string, error)) { 37 | std::cerr << error << std::endl; 38 | return 1; 39 | } 40 | 41 | const size_t stack_size{32}; 42 | uint8_t expected_result[] = { 43 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 6, 7, 8, 44 | }; 45 | 46 | bool success = true; 47 | 48 | std::unique_ptr vm(ubpf_create(), ubpf_destroy); 49 | if (!ubpf_setup_custom_test( 50 | vm, 51 | program_string, 52 | [](ubpf_vm_up& vm, std::string& error) { 53 | if (ubpf_register_stack_usage_calculator(vm.get(), stack_usage_calculator, nullptr) < 0) { 54 | error = "Failed to register stack usage calculator."; 55 | return false; 56 | } 57 | return true; 58 | }, 59 | jit_fn, 60 | error)) { 61 | std::cerr << "Problem setting up custom test: " << error << std::endl; 62 | return 1; 63 | } 64 | 65 | char* ex_jit_compile_error = nullptr; 66 | auto jit_ex_fn = ubpf_compile_ex(vm.get(), &ex_jit_compile_error, ExtendedJitMode); 67 | uint8_t external_stack[stack_size] = { 68 | 0, 69 | }; 70 | 71 | jit_result = jit_ex_fn(nullptr, 0, external_stack, stack_size); 72 | 73 | if (jit_result) { 74 | std::cerr << "Execution of the JIT'd program gave a non-0 result.\n"; 75 | return 1; 76 | } 77 | 78 | for (size_t i = 0; i < stack_size; i++) { 79 | if (external_stack[i] != expected_result[i]) { 80 | std::cerr << "Byte 0x" << std::hex << i << " different between expected (0x" << (uint32_t)expected_result[i] 81 | << ") and actual (0x" << (uint32_t)external_stack[i] << ")\n"; 82 | success = false; 83 | } 84 | } 85 | 86 | if (!success) { 87 | return !success; 88 | } 89 | 90 | std::memset(external_stack, 0x0, sizeof(external_stack)); 91 | int interp_success{ubpf_exec_ex(vm.get(), nullptr, 0, &interp_result, external_stack, stack_size)}; 92 | 93 | if (interp_success < 0) { 94 | std::cerr << "There was an error interpreting the program: " << success << "\n"; 95 | return 1; 96 | } 97 | 98 | if (interp_result) { 99 | std::cerr << "Execution of the interpreted program gave a non-0 result.\n"; 100 | return 1; 101 | } 102 | 103 | for (size_t i = 0; i < stack_size; i++) { 104 | if (external_stack[i] != expected_result[i]) { 105 | std::cerr << "Byte 0x" << std::hex << i << " different between expected (0x" << (uint32_t)expected_result[i] 106 | << ") and actual (0x" << (uint32_t)external_stack[i] << ")\n"; 107 | success = false; 108 | } 109 | } 110 | 111 | if (!success) { 112 | return !success; 113 | } 114 | 115 | return 0; 116 | } 117 | -------------------------------------------------------------------------------- /custom_tests/srcs/ubpf_test_debug_function.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) Will Hawkins 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | extern "C" 12 | { 13 | #include "ubpf.h" 14 | } 15 | 16 | #include "ubpf_custom_test_support.h" 17 | 18 | typedef struct _vm_state { 19 | int pc; 20 | std::vector registers; 21 | std::vector stack; 22 | } vm_state_t; 23 | 24 | void 25 | debug_callout(void* context, int program_counter, const uint64_t registers[16], const uint8_t* stack_start, size_t stack_length, uint64_t register_mask, const uint8_t* stack_mask) 26 | { 27 | UNREFERENCED_PARAMETER(register_mask); 28 | UNREFERENCED_PARAMETER(stack_mask); 29 | std::vector* vm_states = static_cast*>(context); 30 | vm_state_t vm_state{}; 31 | 32 | vm_state.pc = program_counter; 33 | for (int i = 0; i < 16; i++) { 34 | vm_state.registers.push_back(registers[i]); 35 | } 36 | for (size_t i = 0; i < stack_length; i++) { 37 | vm_state.stack.push_back(stack_start[i]); 38 | } 39 | 40 | vm_states->push_back(vm_state); 41 | } 42 | 43 | uint64_t test_function_1(uint64_t r1, uint64_t r2, uint64_t r3, uint64_t r4, uint64_t r5) 44 | { 45 | return r1 + r2 + r3 + r4 + r5; 46 | } 47 | 48 | int 49 | main(int argc, char** argv) 50 | { 51 | std::string program_string{}; 52 | std::string error{}; 53 | ubpf_jit_fn jit_fn; 54 | 55 | std::vector vm_states; 56 | 57 | if (!get_program_string(argc, argv, program_string, error)) { 58 | std::cerr << error << std::endl; 59 | return 1; 60 | } 61 | 62 | uint64_t memory{0x123456789}; 63 | 64 | std::unique_ptr vm(ubpf_create(), ubpf_destroy); 65 | if (!ubpf_setup_custom_test( 66 | vm, 67 | program_string, 68 | [](ubpf_vm_up& vm, std::string& error) { 69 | int retval = ubpf_register(vm.get(), 1, "test_function_1", test_function_1); 70 | if (retval < 0) { 71 | error = "Problem registering test function retval=" + std::to_string(retval); 72 | return false; 73 | } 74 | return true; 75 | }, 76 | jit_fn, 77 | error)) { 78 | std::cerr << "Problem setting up custom test: " << error << std::endl; 79 | return 1; 80 | } 81 | 82 | if (ubpf_register_debug_fn(vm.get(), &vm_states, debug_callout) < 0) { 83 | std::cerr << "Problem registering debug function" << std::endl; 84 | return 1; 85 | } 86 | 87 | uint64_t bpf_return_value; 88 | if (ubpf_exec(vm.get(), &memory, sizeof(memory), &bpf_return_value)) { 89 | std::cerr << "Problem executing program" << std::endl; 90 | return 1; 91 | } 92 | 93 | if (vm_states.empty()) { 94 | std::cerr << "No debug callouts were made" << std::endl; 95 | return 1; 96 | } 97 | 98 | for (auto& vm_state : vm_states) { 99 | std::cout << "Program Counter: " << vm_state.pc << std::endl; 100 | for (int i = 0; i < 16; i++) { 101 | std::cout << "Register " << i << ": " << vm_state.registers[i] << std::endl; 102 | } 103 | std::cout << "Stack: "; 104 | for (auto& stack_byte : vm_state.stack) { 105 | std::cout << std::hex << static_cast(stack_byte) << " "; 106 | } 107 | std::cout << std::endl; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /custom_tests/srcs/ubpf_test_default_dispatcher_helper_context.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) Will Hawkins 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | extern "C" 11 | { 12 | #include "ubpf.h" 13 | } 14 | 15 | #include "ubpf_custom_test_support.h" 16 | 17 | uint64_t 18 | simple_helper(uint64_t p0, uint64_t p1, uint64_t p2, uint64_t p3, uint64_t p4, void* cookie) 19 | { 20 | UNREFERENCED_PARAMETER(p0); 21 | UNREFERENCED_PARAMETER(p1); 22 | UNREFERENCED_PARAMETER(p2); 23 | UNREFERENCED_PARAMETER(p3); 24 | UNREFERENCED_PARAMETER(p4); 25 | uint64_t* ccookie = (uint64_t*)cookie; 26 | return *ccookie; 27 | } 28 | 29 | int 30 | main(int argc, char** argv) 31 | { 32 | std::string program_string{}; 33 | std::string error{}; 34 | ubpf_jit_fn jit_fn; 35 | uint64_t memory{0x123456789}; 36 | 37 | // The test program invokes an external function at index 1 (which is invoked via the 38 | // default external helper dispatcher). The result of that external function is given as the 39 | // result of the eBPF's program execution. Therefore, ... 40 | if (!get_program_string(argc, argv, program_string, error)) { 41 | std::cerr << error << std::endl; 42 | return 1; 43 | } 44 | 45 | std::unique_ptr vm(ubpf_create(), ubpf_destroy); 46 | if (!ubpf_setup_custom_test( 47 | vm, 48 | program_string, 49 | [](ubpf_vm_up& vm, std::string& error) { 50 | if (ubpf_register(vm.get(), 1, "simple helper", as_external_function_t((void*)simple_helper)) < 0) { 51 | error = "Failed to register external helper function at index 1."; 52 | return false; 53 | } 54 | return true; 55 | }, 56 | jit_fn, 57 | error)) { 58 | std::cerr << "Problem setting up custom test: " << error << std::endl; 59 | return 1; 60 | } 61 | 62 | [[maybe_unused]] auto result = jit_fn(&memory, sizeof(uint64_t)); 63 | 64 | // ... because of the semantics of external helper functions, the result of the eBPF 65 | // program execution should point to the same place to which &memory points. 66 | if (result != memory) { 67 | std::cerr << "result and memory are not equal (JIT version).\n"; 68 | return 1; 69 | } 70 | 71 | if (ubpf_exec(vm.get(), &memory, sizeof(uint64_t), &result)) { 72 | std::cerr << "There was an error interpreting the test program.\n"; 73 | return 1; 74 | } 75 | 76 | if (result != memory) { 77 | std::cerr << "result and memory are not equal (interpreter version).\n"; 78 | return 1; 79 | } 80 | return 0; 81 | } 82 | -------------------------------------------------------------------------------- /custom_tests/srcs/ubpf_test_default_local_function_stack_size.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) Will Hawkins 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #include "ubpf_int.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | extern "C" 12 | { 13 | #include "ubpf.h" 14 | } 15 | 16 | #include "ubpf_custom_test_support.h" 17 | 18 | int 19 | main(int argc, char** argv) 20 | { 21 | std::string program_string{}; 22 | std::string error{}; 23 | ubpf_jit_fn jit_fn; 24 | uint64_t jit_result{}; 25 | uint64_t interp_result{}; 26 | 27 | if (!get_program_string(argc, argv, program_string, error)) { 28 | std::cerr << error << std::endl; 29 | return 1; 30 | } 31 | 32 | const size_t stack_size{8192}; 33 | 34 | uint8_t expected_result[8192] = { 35 | 0, 36 | }; 37 | 38 | expected_result[stack_size - 1 - 0] = 0x1; 39 | expected_result[stack_size - 1 - 1] = 0x2; 40 | expected_result[stack_size - 1 - 2] = 0x3; 41 | expected_result[stack_size - 1 - 3] = 0x4; 42 | 43 | 44 | expected_result[stack_size - UBPF_EBPF_LOCAL_FUNCTION_STACK_SIZE - 1 - 0] = 0x11; 45 | expected_result[stack_size - UBPF_EBPF_LOCAL_FUNCTION_STACK_SIZE - 1 - 1] = 0x12; 46 | expected_result[stack_size - UBPF_EBPF_LOCAL_FUNCTION_STACK_SIZE - 1 - 2] = 0x13; 47 | expected_result[stack_size - UBPF_EBPF_LOCAL_FUNCTION_STACK_SIZE - 1 - 3] = 0x14; 48 | 49 | bool success = true; 50 | 51 | std::unique_ptr vm(ubpf_create(), ubpf_destroy); 52 | if (!ubpf_setup_custom_test( 53 | vm, 54 | program_string, 55 | [](ubpf_vm_up& vm, std::string& error) { 56 | UNUSED_PARAMETER(vm); 57 | UNUSED_PARAMETER(error); 58 | return true; 59 | }, 60 | jit_fn, 61 | error)) { 62 | std::cerr << "Problem setting up custom test: " << error << std::endl; 63 | return 1; 64 | } 65 | 66 | char* ex_jit_compile_error = nullptr; 67 | auto jit_ex_fn = ubpf_compile_ex(vm.get(), &ex_jit_compile_error, ExtendedJitMode); 68 | uint8_t external_stack[stack_size] = { 69 | 0, 70 | }; 71 | jit_result = jit_ex_fn(nullptr, 0, external_stack, stack_size); 72 | 73 | if (jit_result) { 74 | std::cerr << "Execution of the JIT'd program gave a non-0 result.\n"; 75 | return 1; 76 | } 77 | 78 | for (size_t i = 0; i < stack_size; i++) { 79 | if (external_stack[i] != expected_result[i]) { 80 | std::cerr << "Byte 0x" << std::hex << i << " different between expected (0x" << (uint32_t)expected_result[i] 81 | << ") and actual (0x" << (uint32_t)external_stack[i] << ")\n"; 82 | success = false; 83 | } 84 | } 85 | 86 | if (!success) { 87 | return !success; 88 | } 89 | 90 | std::memset(external_stack, 0x0, sizeof(external_stack)); 91 | int interp_success{ubpf_exec_ex(vm.get(), nullptr, 0, &interp_result, external_stack, stack_size)}; 92 | 93 | if (interp_success < 0) { 94 | std::cerr << "There was an error interpreting the program: " << success << "\n"; 95 | return 1; 96 | } 97 | 98 | if (interp_result) { 99 | std::cerr << "Execution of the interpreted program gave a non-0 result.\n"; 100 | return 1; 101 | } 102 | 103 | for (size_t i = 0; i < stack_size; i++) { 104 | if (external_stack[i] != expected_result[i]) { 105 | std::cerr << "Byte 0x" << std::hex << i << " different between expected (0x" << (uint32_t)expected_result[i] 106 | << ") and actual (0x" << (uint32_t)external_stack[i] << ")\n"; 107 | success = false; 108 | } 109 | } 110 | 111 | if (!success) { 112 | return !success; 113 | } 114 | 115 | return 0; 116 | } 117 | -------------------------------------------------------------------------------- /custom_tests/srcs/ubpf_test_external_dispatcher_context_overwrite.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) Will Hawkins 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | extern "C" 11 | { 12 | #include "ubpf.h" 13 | } 14 | 15 | #include "ubpf_custom_test_support.h" 16 | 17 | uint64_t *cookie_pointer_value{nullptr}; 18 | uint64_t 19 | external_dispatcher(uint64_t p0, uint64_t p1, uint64_t p2, uint64_t p3, uint64_t p4, unsigned int idx, void* cookie) 20 | { 21 | UNREFERENCED_PARAMETER(p0); 22 | UNREFERENCED_PARAMETER(p1); 23 | UNREFERENCED_PARAMETER(p2); 24 | UNREFERENCED_PARAMETER(p3); 25 | UNREFERENCED_PARAMETER(p4); 26 | UNREFERENCED_PARAMETER(idx); 27 | uint64_t* ccookie = (uint64_t*)cookie; 28 | cookie_pointer_value = ccookie; 29 | return 1; 30 | } 31 | 32 | bool 33 | external_dispatcher_validater(unsigned int idx, const struct ubpf_vm* cookie) 34 | { 35 | UNREFERENCED_PARAMETER(idx); 36 | UNREFERENCED_PARAMETER(cookie); 37 | return true; 38 | } 39 | 40 | int main(int argc, char **argv) 41 | { 42 | std::string program_string{}; 43 | std::string error{}; 44 | ubpf_jit_fn jit_fn; 45 | uint64_t memory{0x123456789}; 46 | 47 | // The program modifies (eBPF) r0 (see test description) and then invokes 48 | // a helper function that will be invoked through the external 49 | // dispatcher. 50 | if (!get_program_string(argc, argv, program_string, error)) { 51 | std::cerr << error << std::endl; 52 | return 1; 53 | } 54 | 55 | std::unique_ptr vm(ubpf_create(), ubpf_destroy); 56 | if (!ubpf_setup_custom_test( 57 | vm, 58 | program_string, 59 | [](ubpf_vm_up& vm, std::string &error) { 60 | if (ubpf_register_external_dispatcher(vm.get(), external_dispatcher, external_dispatcher_validater) < 0) { 61 | error = "Failed to register external dispatcher."; 62 | return false; 63 | } 64 | return true; 65 | }, 66 | jit_fn, 67 | error)) { 68 | std::cerr << "Problem setting up custom test: " << error << std::endl; 69 | return 1; 70 | } 71 | 72 | [[maybe_unused]] auto result = jit_fn(&memory, sizeof(uint64_t)); 73 | 74 | // Ultimately, the cookie pointer that we got as context to the external helper dispatcher 75 | // should match what we passed as argument 0 to jit_fn (even though (eBPF) r0 has been 76 | // modified by the eBPF program). 77 | return !(cookie_pointer_value == &memory); 78 | } 79 | -------------------------------------------------------------------------------- /custom_tests/srcs/ubpf_test_external_dispatcher_simple_context.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) Will Hawkins 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | extern "C" 11 | { 12 | #include "ubpf.h" 13 | } 14 | 15 | #include "ubpf_custom_test_support.h" 16 | 17 | uint64_t 18 | external_dispatcher(uint64_t p0, uint64_t p1, uint64_t p2, uint64_t p3, uint64_t p4, unsigned int idx, void* cookie) 19 | { 20 | UNREFERENCED_PARAMETER(p0); 21 | UNREFERENCED_PARAMETER(p1); 22 | UNREFERENCED_PARAMETER(p2); 23 | UNREFERENCED_PARAMETER(p3); 24 | UNREFERENCED_PARAMETER(p4); 25 | UNREFERENCED_PARAMETER(idx); 26 | uint64_t* ccookie = (uint64_t*)cookie; 27 | return *ccookie; 28 | } 29 | 30 | bool 31 | external_dispatcher_validater(unsigned int idx, const struct ubpf_vm* cookie) 32 | { 33 | UNREFERENCED_PARAMETER(idx); 34 | UNREFERENCED_PARAMETER(cookie); 35 | return true; 36 | } 37 | 38 | int main(int argc, char **argv) 39 | { 40 | std::string program_string{}; 41 | std::string error{}; 42 | ubpf_jit_fn jit_fn; 43 | uint64_t memory{0x123456789}; 44 | 45 | // The test program invokes an external function (which is invoked via the registered 46 | // external helper dispatcher). The result of that external function is given as the 47 | // result of the eBPF's program execution. Therefore, ... 48 | if (!get_program_string(argc, argv, program_string, error)) { 49 | std::cerr << error << std::endl; 50 | return 1; 51 | } 52 | 53 | std::unique_ptr vm(ubpf_create(), ubpf_destroy); 54 | if (!ubpf_setup_custom_test( 55 | vm, 56 | program_string, 57 | [](ubpf_vm_up& vm, std::string &error) { 58 | if (ubpf_register_external_dispatcher(vm.get(), external_dispatcher, external_dispatcher_validater) < 0) { 59 | error = "Failed to register external dispatcher."; 60 | return false; 61 | } 62 | return true; 63 | }, 64 | jit_fn, 65 | error)) { 66 | std::cerr << "Problem setting up custom test: " << error << std::endl; 67 | return 1; 68 | } 69 | 70 | [[maybe_unused]] auto result = jit_fn(&memory, sizeof(uint64_t)); 71 | 72 | // ... because of the semantics of external_dispatcher, the result of the eBPF 73 | // program execution should point to the same place to which &memory points. 74 | return !(result == memory); 75 | } 76 | -------------------------------------------------------------------------------- /custom_tests/srcs/ubpf_test_frame_pointer.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) Will Hawkins 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | extern "C" 12 | { 13 | #include "ubpf.h" 14 | } 15 | 16 | #include "ubpf_custom_test_support.h" 17 | 18 | int 19 | stack_usage_calculator(const struct ubpf_vm* vm, uint16_t pc, void* cookie) 20 | { 21 | UNREFERENCED_PARAMETER(vm); 22 | UNREFERENCED_PARAMETER(pc); 23 | UNREFERENCED_PARAMETER(cookie); 24 | return 16; 25 | } 26 | 27 | int 28 | overwrite_stack_usage_calculator(const struct ubpf_vm* vm, uint16_t pc, void* cookie) 29 | { 30 | UNREFERENCED_PARAMETER(vm); 31 | UNREFERENCED_PARAMETER(pc); 32 | UNREFERENCED_PARAMETER(cookie); 33 | return 0; 34 | } 35 | 36 | int 37 | main(int argc, char** argv) 38 | { 39 | std::string program_string{}; 40 | std::string error{}; 41 | ubpf_jit_fn jit_fn; 42 | 43 | if (!get_program_string(argc, argv, program_string, error)) { 44 | std::cerr << error << std::endl; 45 | return 1; 46 | } 47 | 48 | uint64_t no_overwrite_interp_result = 0; 49 | uint64_t no_overwrite_jit_result = 0; 50 | uint64_t overwrite_interp_result = 0; 51 | uint64_t overwrite_jit_result = 0; 52 | 53 | { 54 | 55 | std::unique_ptr vm(ubpf_create(), ubpf_destroy); 56 | if (!ubpf_setup_custom_test( 57 | vm, 58 | program_string, 59 | [](ubpf_vm_up& vm, std::string& error) { 60 | if (ubpf_register_stack_usage_calculator(vm.get(), stack_usage_calculator, nullptr) < 0) { 61 | error = "Failed to register stack usage calculator."; 62 | return false; 63 | } 64 | return true; 65 | }, 66 | jit_fn, 67 | error)) { 68 | std::cerr << "Problem setting up custom test: " << error << std::endl; 69 | return 1; 70 | } 71 | 72 | no_overwrite_jit_result = jit_fn(nullptr, 0); 73 | [[maybe_unused]] auto exec_result = ubpf_exec(vm.get(), NULL, 0, &no_overwrite_interp_result); 74 | } 75 | 76 | { 77 | 78 | std::unique_ptr vm(ubpf_create(), ubpf_destroy); 79 | std::string error{}; 80 | if (!ubpf_setup_custom_test( 81 | vm, 82 | program_string, 83 | [](ubpf_vm_up& vm, std::string& error) { 84 | if (ubpf_register_stack_usage_calculator(vm.get(), overwrite_stack_usage_calculator, nullptr) < 0) { 85 | error = "Failed to register stack usage calculator."; 86 | return false; 87 | } 88 | return true; 89 | }, 90 | jit_fn, 91 | error)) { 92 | std::cerr << "Problem setting up custom test: " << error << std::endl; 93 | return 1; 94 | } 95 | 96 | overwrite_jit_result = jit_fn(nullptr, 0); 97 | 98 | [[maybe_unused]] auto exec_result = ubpf_exec(vm.get(), NULL, 0, &overwrite_interp_result); 99 | } 100 | // ... because of the semantics of external_dispatcher, the result of the eBPF 101 | // program execution should point to the same place to which &memory points. 102 | return !( 103 | no_overwrite_interp_result == no_overwrite_jit_result && no_overwrite_interp_result == 0x5 && 104 | overwrite_interp_result == overwrite_jit_result && overwrite_interp_result == 0x37); 105 | } 106 | -------------------------------------------------------------------------------- /custom_tests/srcs/ubpf_test_jit_buffer_too_small.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) Will Hawkins 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #include 5 | #include 6 | 7 | extern "C" 8 | { 9 | #include "ebpf.h" 10 | #include "ubpf.h" 11 | } 12 | 13 | #include "ubpf_custom_test_support.h" 14 | 15 | /** 16 | * @brief This program reads BPF instructions from stdin and memory contents from 17 | * the first agument. It then executes the BPF program and prints the 18 | * value of %r0 at the end of execution. 19 | */ 20 | int main() 21 | { 22 | std::string expected_error{"Failed to compile: Target buffer too small"}; 23 | std::string program_string{"95 00 00 00 00 00 00 00"}; 24 | ubpf_jit_fn jit_fn; 25 | 26 | std::vector program = bytes_to_ebpf_inst(base16_decode(program_string)); 27 | 28 | ubpf_vm_up vm(ubpf_create(), ubpf_destroy); 29 | std::string error{}; 30 | char *error_s{nullptr}; 31 | 32 | if (!ubpf_setup_custom_test( 33 | vm, 34 | program_string, 35 | custom_test_fixup_cb{[](ubpf_vm_up& vm, std::string& error) { 36 | if (ubpf_set_jit_code_size(vm.get(), 1) < 0) { 37 | error = "Could not set the jit code size."; 38 | return false; 39 | } 40 | return true; 41 | }}, 42 | jit_fn, 43 | error)) { 44 | free(error_s); 45 | 46 | // Only if the error is that the buffer was too small does this test pass. 47 | if (jit_fn == nullptr && expected_error == error) 48 | return 0; 49 | } 50 | 51 | free(error_s); 52 | return 1; 53 | } 54 | -------------------------------------------------------------------------------- /custom_tests/srcs/ubpf_test_jit_unexpected_instruction.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) Will Hawkins 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | extern "C" 9 | { 10 | #include "ebpf.h" 11 | #include "ubpf.h" 12 | } 13 | 14 | #include "ubpf_custom_test_support.h" 15 | 16 | int main(int argc, char** argv) 17 | { 18 | std::string expected_error{"Failed to load program: unknown opcode 0x8f at PC 0" }; 19 | ubpf_jit_fn jit_fn; 20 | std::string program_string; 21 | std::string error{}; 22 | 23 | // The program's first instruction contains an invalid opcode. Attempting to load this 24 | // program should elicit an error alerting the user to an unknown opcode (see above). 25 | if (!get_program_string(argc, argv, program_string, error)) { 26 | std::cerr << error << std::endl; 27 | return 1; 28 | } 29 | 30 | std::vector program = bytes_to_ebpf_inst(base16_decode(program_string)); 31 | 32 | ubpf_vm_up vm(ubpf_create(), ubpf_destroy); 33 | 34 | if (!ubpf_setup_custom_test( 35 | vm, 36 | program_string, 37 | custom_test_fixup_cb{[](ubpf_vm_up&, std::string& ) { 38 | return true; 39 | }}, 40 | jit_fn, 41 | error)) { 42 | 43 | // Only if the error matches exactly what we expect should this test pass. 44 | if (jit_fn == nullptr && expected_error == error) 45 | return 0; 46 | } 47 | 48 | return 1; 49 | } 50 | -------------------------------------------------------------------------------- /custom_tests/srcs/ubpf_test_update_dispatcher.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | extern "C" 11 | { 12 | #include "ubpf.h" 13 | } 14 | 15 | #include "ubpf_custom_test_support.h" 16 | 17 | const uint64_t dispatcher_test_dispatcher_failure{40}; 18 | const uint64_t dispatcher_test_dispatcher_success{42}; 19 | const uint64_t updated_dispatcher_test_dispatcher_failure{41}; 20 | const uint64_t updated_dispatcher_test_dispatcher_success{43}; 21 | 22 | uint64_t 23 | dispatcher_test_dispatcher( 24 | uint64_t p0, uint64_t p1, uint64_t p2, uint64_t p3, uint64_t p4, unsigned int idx, void* cookie) 25 | { 26 | UNREFERENCED_PARAMETER(p0); 27 | UNREFERENCED_PARAMETER(p1); 28 | UNREFERENCED_PARAMETER(p2); 29 | UNREFERENCED_PARAMETER(p3); 30 | UNREFERENCED_PARAMETER(p4); 31 | UNREFERENCED_PARAMETER(cookie); 32 | if (idx != 1) { 33 | return dispatcher_test_dispatcher_failure; 34 | } 35 | return dispatcher_test_dispatcher_success; 36 | } 37 | 38 | uint64_t 39 | updated_dispatcher_test_dispatcher( 40 | uint64_t p0, uint64_t p1, uint64_t p2, uint64_t p3, uint64_t p4, unsigned int idx, void* cookie) 41 | { 42 | UNREFERENCED_PARAMETER(p0); 43 | UNREFERENCED_PARAMETER(p1); 44 | UNREFERENCED_PARAMETER(p2); 45 | UNREFERENCED_PARAMETER(p3); 46 | UNREFERENCED_PARAMETER(p4); 47 | UNREFERENCED_PARAMETER(cookie); 48 | if (idx != 1) { 49 | return updated_dispatcher_test_dispatcher_failure; 50 | } 51 | return updated_dispatcher_test_dispatcher_success; 52 | } 53 | 54 | bool 55 | test_helpers_validater(unsigned int idx, const struct ubpf_vm* vm) 56 | { 57 | UNREFERENCED_PARAMETER(idx); 58 | UNREFERENCED_PARAMETER(vm); 59 | return true; 60 | } 61 | 62 | int 63 | main(int argc, char** argv) 64 | { 65 | std::string program_string; 66 | std::string error{}; 67 | std::string memory_string; 68 | 69 | if (!get_program_string(argc, argv, program_string, error)) { 70 | std::cerr << error << std::endl; 71 | return 1; 72 | } 73 | 74 | ubpf_jit_fn jit_fn; 75 | uint64_t memory{0x123456789}; 76 | std::unique_ptr vm(ubpf_create(), ubpf_destroy); 77 | if (!ubpf_setup_custom_test( 78 | vm, 79 | program_string, 80 | [](ubpf_vm_up& vm, std::string& error) { 81 | if (ubpf_register_external_dispatcher(vm.get(), dispatcher_test_dispatcher, test_helpers_validater)) { 82 | error = "Failed to register the external dispatcher function"; 83 | return false; 84 | } 85 | return true; 86 | }, 87 | jit_fn, 88 | error)) { 89 | std::cerr << "Problem setting up custom test: " << error << std::endl; 90 | return 1; 91 | } 92 | 93 | auto first_result = jit_fn(&memory, sizeof(uint64_t)); 94 | 95 | if (ubpf_register_external_dispatcher(vm.get(), updated_dispatcher_test_dispatcher, test_helpers_validater)) { 96 | std::cout << "Failed to register updated dispatcher function\n"; 97 | return 1; 98 | } 99 | 100 | auto second_result = jit_fn(&memory, sizeof(uint64_t)); 101 | 102 | auto current_success{ 103 | (first_result == dispatcher_test_dispatcher_success && 104 | second_result == updated_dispatcher_test_dispatcher_success)}; 105 | return current_success ? 0 : 1; 106 | } 107 | -------------------------------------------------------------------------------- /libfuzzer/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | if (UBPF_SKIP_EXTERNAL) 5 | message(WARNING "Skipping configuration of tests that require external package support.") 6 | return() 7 | endif() 8 | 9 | if (PLATFORM_WINDOWS) 10 | set(Boost_INCLUDE_DIRS ${CMAKE_BINARY_DIR}/packages/boost/lib/native/include) 11 | set(Boost_LIBRARY_DIRS ${CMAKE_BINARY_DIR}/packages/boost_filesystem-vc143/lib/native) 12 | endif() 13 | 14 | set(UBPF_FUZZER_INCLUDES "${CMAKE_SOURCE_DIR}/vm" 15 | "${CMAKE_BINARY_DIR}/vm" 16 | "${CMAKE_BINARY_DIR}/_deps/gsl-src/include" 17 | "${CMAKE_SOURCE_DIR}/vm/inc" 18 | "${CMAKE_BINARY_DIR}/vm/inc" 19 | "${CMAKE_SOURCE_DIR}/ubpf_plugin" 20 | "${CMAKE_SOURCE_DIR}/external/ebpf-verifier/src" 21 | "${CMAKE_SOURCE_DIR}/external/ebpf-verifier/src/crab" 22 | "${CMAKE_SOURCE_DIR}/external/ebpf-verifier/src/crab_utils" 23 | "${CMAKE_CURRENT_BINARY_DIR}" 24 | "${Boost_INCLUDE_DIRS}") 25 | 26 | set(UBPF_FUZZER_LIBS 27 | ubpf 28 | ubpf_settings 29 | ebpfverifier) 30 | 31 | set(CMAKE_REQUIRED_INCLUDES ${UBPF_FUZZER_INCLUDES}) 32 | 33 | set(CMAKE_CXX_STANDARD 20) 34 | 35 | configure_file( 36 | libfuzzer_config.h.inc 37 | "${CMAKE_CURRENT_BINARY_DIR}/libfuzzer_config.h" 38 | ) 39 | 40 | add_executable( 41 | ubpf_fuzzer 42 | libfuzz_harness.cc 43 | ) 44 | 45 | target_include_directories("ubpf_fuzzer" PRIVATE ${UBPF_FUZZER_INCLUDES}) 46 | 47 | if (PLATFORM_WINDOWS) 48 | set(CMAKE_EXE_LINKER_FLAGS_FUZZERDEBUG libsancov.lib clang_rt.fuzzer_MDd-x86_64.lib) 49 | endif() 50 | 51 | target_link_libraries(ubpf_fuzzer PRIVATE ${UBPF_FUZZER_LIBS}) 52 | -------------------------------------------------------------------------------- /libfuzzer/libfuzzer_config.h.inc: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2022-present, IO Visor Project 3 | All rights reserved. 4 | 5 | This source code is licensed in accordance with the terms specified in 6 | the LICENSE file found in the root directory of this source tree. 7 | */ 8 | 9 | #pragma once 10 | 11 | #cmakedefine HAVE_EBPF_VERIFIER_CHECK_CONSTRAINTS_AT_LABEL 12 | -------------------------------------------------------------------------------- /libfuzzer/split.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Split the file name into path and base name 4 | path=$(dirname $1) 5 | base=$(basename $1) 6 | 7 | # Get the first 4 bytes from the file (which is the length of the program) 8 | input="$(xxd -p -l 4 $1)" 9 | # Convert from little endian 10 | input="${input:6:2}${input:4:2}${input:2:2}${input:0:2}" 11 | 12 | # Convert input from hex string to value 13 | length=$((16#$input)) 14 | 15 | # Extract the hash part from the file name 16 | hash=$(echo $base | cut -d'-' -f2-) 17 | 18 | # Copy the program to a file named program-$hash 19 | echo "Extracting program-$hash..." 20 | dd if=$1 of=$path/program-$hash bs=1 skip=4 count=$length 2> /dev/null 21 | 22 | echo "Extracting memory-$hash..." 23 | # Copy the rest to a file named memory-$hash 24 | dd if=$1 of=$path/memory-$hash bs=1 skip=$((4 + $length)) 2> /dev/null 25 | 26 | echo "Disassembling program-$hash..." 27 | # Unassembly program using bin/ubpf-disassembler 28 | bin/ubpf-disassembler $path/program-$hash > $path/program-$hash.asm 29 | 30 | echo "Program size: $(stat -c %s $path/program-$hash)" 31 | echo "Memory size: $(stat -c %s $path/memory-$hash)" 32 | 33 | echo "Disassembled program:" 34 | cat $path/program-$hash.asm 35 | 36 | echo "Memory contents:" 37 | xxd $path/memory-$hash 38 | -------------------------------------------------------------------------------- /mainpage.dox: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | /** 5 | * @mainpage 6 | * 7 | * @section intro Introduction 8 | * This project aims to create an Apache-licensed library for executing eBPF programs. The primary implementation of 9 | * eBPF lives in the Linux kernel, but due to its GPL license it can't be used in many projects. 10 | * 11 | * For more info see: 12 | * 16 | * 17 | * This project includes an eBPF assembler, disassembler, interpreter (for all platforms), and JIT compiler (for x86-64 and Arm64 targets). 18 | * 19 | * @section build Building 20 | * Note: This works on Windows, Linux, and MacOS, provided the prerequisites are installed. 21 | *
    22 | *
  • cmake -S . -B build -DUBPF_ENABLE_TESTS=true 23 | *
  • cmake --build build --config Debug 24 | *
25 | 26 | * @section api uBPF API Reference 27 | * API docs: [**ubpf.h**](ubpf_8h.html). 28 | * 29 | */ 30 | 31 | 32 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # pypi version of parcon does not support python3 2 | git+https://github.com/javawizard/parcon 3 | nose ~= 1.3.7 4 | pyelftools ~= 0.32 5 | -------------------------------------------------------------------------------- /scripts/.check-license.ignore: -------------------------------------------------------------------------------- 1 | # This file uses regular expressions, not shell globs! 2 | 3 | # File extensions that don't support embedding license info 4 | .*\.md$ 5 | .*\.o$ 6 | .*\.png$ 7 | .*\.proj$ 8 | .*\.rc$ 9 | .*\.sln$ 10 | .*\.vsdx$ 11 | 12 | # Generated files 13 | tools/netsh/resource.h 14 | 15 | # Other Files 16 | LICENSE.txt 17 | Doxyfile 18 | \.check-license\.ignore 19 | \.clang-format 20 | \.gitattributes 21 | \.github/CODEOWNERS 22 | \.github/workflows/build.yml 23 | \.gitignore 24 | \.gitmodules 25 | packages.config -------------------------------------------------------------------------------- /scripts/build-libbpf.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | # Copyright (c) Microsoft Corporation 3 | # SPDX-License-Identifier: MIT 4 | 5 | git clone https://github.com/libbpf/libbpf.git 6 | if [ $? -ne 0 ]; then 7 | echo "Could not clone the libbpf repository." 8 | exit 1 9 | fi 10 | 11 | # Jump in to the src directory to do the actual build. 12 | cd libbpf/src 13 | 14 | make 15 | if [ $? -ne 0 ]; then 16 | echo "Could not build libbpf source." 17 | exit 1 18 | fi 19 | 20 | # Now that the build was successful, install the library (shared 21 | # object and header files) in a spot where FindLibBpf.cmake can 22 | # find it when it is being built. 23 | sudo PREFIX=/usr LIBDIR=/usr/lib/x86_64-linux-gnu/ make install 24 | exit 0 25 | -------------------------------------------------------------------------------- /scripts/check-license.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright (c) Microsoft Corporation 4 | # SPDX-License-Identifier: Apache-2.0 5 | 6 | # This script accepts either a list of files relative to the current 7 | # working directory, or it will check all files tracked in Git. In 8 | # both cases, it ignores files matching any regular expression listed 9 | # in '.check-license.ignore'. 10 | 11 | set -o errexit 12 | set -o pipefail 13 | 14 | license=("Copyright" "SPDX-License-Identifier:") 15 | 16 | root=$(git rev-parse --show-toplevel) 17 | 18 | # If we are inside mingw* environment, then we update the path to proper format 19 | if [[ $(uname) == MINGW* ]] ; then 20 | root=$(cygpath -u "${root}") 21 | fi 22 | 23 | ignore_res=() 24 | while IFS=$'\r' read -r i; do 25 | if [[ $i =~ ^# ]] || [[ -z $i ]]; then # ignore comments 26 | continue 27 | fi 28 | ignore_res+=("$i") 29 | done < "$root/scripts/.check-license.ignore" 30 | 31 | should_ignore() { 32 | for re in "${ignore_res[@]}"; do 33 | if [[ $1 =~ $re ]]; then 34 | return 35 | fi 36 | done 37 | false 38 | } 39 | 40 | # Create array of files to check, either from the given arguments or 41 | # all files in Git, ignore any that match a regex in the ignore file. 42 | files=() 43 | if [[ $# -ne 0 ]]; then 44 | for f in "$@"; do 45 | file=$(realpath -q "$f") || file="" 46 | 47 | if [[ ! -f $file ]]; then # skip non-existent files 48 | continue 49 | fi 50 | 51 | file=${file#$root/} # remove the prefix 52 | 53 | if should_ignore "$file"; then 54 | continue 55 | fi 56 | 57 | files+=("$file") 58 | done 59 | else 60 | # Find all files in Git. These are guaranteed to exist, to not be 61 | # generated, and to not have the prefix. 62 | cd "$root" 63 | while IFS= read -r -d '' file; do 64 | if should_ignore "$file"; then 65 | continue 66 | fi 67 | 68 | files+=("$file") 69 | done < <(git ls-files -z) 70 | fi 71 | 72 | failures=0 73 | for file in "${files[@]}"; do 74 | for line in "${license[@]}"; do 75 | # We check only the first four lines to avoid false positives 76 | # (such as this script), but to allow for a shebang and empty 77 | # line between it and the license. 78 | if ! head -n4 "${root}/${file}" | grep --quiet --fixed-strings --max-count=1 "${line}"; then 79 | echo "${file}" missing "${line}" 80 | failures=$((failures + 1)) 81 | break 82 | fi 83 | done 84 | done 85 | 86 | exit $failures 87 | -------------------------------------------------------------------------------- /scripts/commit-msg: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright (c) Microsoft Corporation 4 | # SPDX-License-Identifier: Apache-2.0 5 | 6 | set -o errexit 7 | 8 | exit_() { 9 | echo "" 10 | echo "$1" 11 | echo "" 12 | echo "This hook can be skipped if needed with 'git commit --no-verify'" 13 | echo "See '.git/hooks/commit-msg., installed from 'scripts/commit-msg'" 14 | exit 1 15 | } 16 | 17 | sign_offs="$(grep '^Signed-off-by: ' "$1" || test $? = 1 )" 18 | 19 | if [[ -z $sign_offs ]]; then 20 | exit_ "Commit failed: please sign-off on the DCO with 'git commit -s'" 21 | fi 22 | 23 | if [[ -n $(echo "$sign_offs" | sort | uniq -c | sed -e '/^[ ]*1[ ]/d') ]]; then 24 | exit_ "Commit failed: please remove duplicate Signed-off-by lines" 25 | fi 26 | -------------------------------------------------------------------------------- /scripts/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright (c) Microsoft Corporation 4 | # SPDX-License-Identifier: Apache-2.0 5 | 6 | set -o errexit 7 | 8 | changed_files() 9 | { 10 | files="" 11 | while IFS= read -r line; do 12 | files+=("$line") 13 | done < <(git diff --cached --name-only --diff-filter=ACMR) 14 | } 15 | 16 | exit_() { 17 | echo "" 18 | echo "$1" 19 | echo "" 20 | echo "This hook can be skipped if needed with 'git commit --no-verify'" 21 | echo "See '.git/hooks/pre-commit', installed from 'scripts/pre-commit'" 22 | exit 1 23 | } 24 | 25 | if [[ $(git config --get user.name | wc -w) -lt 2 ]]; then 26 | # This heuristic avoids bad user names such as "root" or "Ubuntu" 27 | # or a computer login name. A full name should (usually) have at 28 | # least two words. We can change this if needed later. 29 | exit_ "Commit failed: please fix your Git user name (see docs/Contributing.md)" 30 | fi 31 | 32 | if ! git diff-index --check --cached HEAD --; then 33 | exit_ "Commit failed: please fix the conflict markers or whitespace errors" 34 | fi 35 | 36 | changed_files 37 | 38 | if [[ ${#files[@]} -eq 0 ]]; then 39 | # When 'git commit --amend' is used, the files list is empty. The 40 | # scripts below interpret an empty file set as a directive to 41 | # check all the files, which is slow (but used in CI). So in this 42 | # Git hook, we just skip the following checks instead. 43 | exit 0 44 | fi 45 | 46 | scripts=$(git rev-parse --show-toplevel)/scripts 47 | 48 | if ! "$scripts/format-code" --quiet --whatif --files="${files[*]}"; then 49 | exit_ "Commit failed: to fix the formatting please run './scripts/format-code --staged' in bash or '.\\scripts\\format-code.ps1 --staged' in powershell" 50 | fi 51 | if ! "$scripts/check-license.sh" "${files[@]}"; then 52 | exit_ "Commit failed: please add license headers to the above files" 53 | fi 54 | -------------------------------------------------------------------------------- /test_framework/expand-testcase.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | Expand testcase into individual files 4 | """ 5 | import os 6 | import sys 7 | import struct 8 | import testdata 9 | import argparse 10 | 11 | ROOT_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..") 12 | if os.path.exists(os.path.join(ROOT_DIR, "ubpf")): 13 | # Running from source tree 14 | sys.path.insert(0, ROOT_DIR) 15 | 16 | import ubpf.assembler 17 | import ubpf.disassembler 18 | 19 | def main(): 20 | parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) 21 | parser.add_argument('name') 22 | parser.add_argument('path') 23 | args = parser.parse_args() 24 | 25 | data = testdata.read(args.name + '.data') 26 | assert data 27 | 28 | if not os.path.isdir(args.path): 29 | os.makedirs(args.path) 30 | 31 | def writefile(name, contents): 32 | open("%s/%s" % (args.path, name), "wb").write(contents) 33 | 34 | if 'mem' in data: 35 | writefile('mem', data['mem']) 36 | 37 | # Probably a packet, so write out a pcap file 38 | writefile('pcap', 39 | struct.pack('=IHHIIIIIIII', 40 | 0xa1b2c3d4, # magic 41 | 2, 4, # version 42 | 0, # time zone offset 43 | 0, # time stamp accuracy 44 | 65535, # snapshot length 45 | 1, # link layer type 46 | 0, 0, # timestamp 47 | len(data['mem']), # length 48 | len(data['mem'])) # length 49 | + data['mem']) 50 | 51 | if 'raw' in data: 52 | code = b''.join(struct.pack("=Q", x) for x in data['raw']) 53 | elif 'asm' in data: 54 | code = ubpf.assembler.assemble(data['asm']) 55 | else: 56 | code = None 57 | 58 | if code: 59 | writefile('code', code) 60 | 61 | if 'asm' in data: 62 | writefile('asm', data['asm'].encode()) 63 | elif code: 64 | writefile('asm', ubpf.disassembler.disassemble(code)) 65 | 66 | if 'pyelf' in data: 67 | from test_elf import generate_elf 68 | elf = generate_elf(data['pyelf']) 69 | writefile('elf', elf) 70 | 71 | if __name__ == "__main__": 72 | main() 73 | -------------------------------------------------------------------------------- /test_framework/test_assembler.py: -------------------------------------------------------------------------------- 1 | import struct 2 | from nose.plugins.skip import Skip, SkipTest 3 | import ubpf.assembler 4 | import ubpf.disassembler 5 | import testdata 6 | try: 7 | xrange 8 | except NameError: 9 | xrange = range 10 | 11 | # Just for assertion messages 12 | def try_disassemble(inst): 13 | data = struct.pack("=Q", inst) 14 | try: 15 | return ubpf.disassembler.disassemble(data).strip() 16 | except ValueError: 17 | return "" 18 | 19 | def check_datafile(filename): 20 | """ 21 | Verify that the result of assembling the 'asm' section matches the 22 | 'raw' section. 23 | """ 24 | data = testdata.read(filename) 25 | if 'asm' not in data: 26 | raise SkipTest("no asm section in datafile") 27 | if 'raw' not in data: 28 | raise SkipTest("no raw section in datafile") 29 | 30 | bin_result = ubpf.assembler.assemble(data['asm']) 31 | assert len(bin_result) % 8 == 0 32 | assert len(bin_result) / 8 == len(data['raw']) 33 | 34 | for i in xrange(0, len(bin_result), 8): 35 | j = int(i/8) 36 | inst, = struct.unpack_from("=Q", bin_result[i:i+8]) 37 | exp = data['raw'][j] 38 | if exp != inst: 39 | raise AssertionError("Expected instruction %d to be %#x (%s), but was %#x (%s)" % 40 | (j, exp, try_disassemble(exp), inst, try_disassemble(inst))) 41 | 42 | def test_datafiles(): 43 | # Nose test generator 44 | # Creates a testcase for each datafile 45 | for filename in testdata.list_files(): 46 | yield check_datafile, filename 47 | -------------------------------------------------------------------------------- /test_framework/test_disassembler.py: -------------------------------------------------------------------------------- 1 | import struct 2 | import difflib 3 | from nose.plugins.skip import Skip, SkipTest 4 | import ubpf.disassembler 5 | import testdata 6 | 7 | def check_datafile(filename): 8 | """ 9 | Verify that the result of disassembling the 'raw' section matches the 10 | 'asm' section. 11 | """ 12 | data = testdata.read(filename) 13 | if 'asm' not in data: 14 | raise SkipTest("no asm section in datafile") 15 | if 'raw' not in data: 16 | raise SkipTest("no raw section in datafile") 17 | 18 | binary = b''.join(struct.pack("=Q", x) for x in data['raw']) 19 | result = ubpf.disassembler.disassemble(binary) 20 | 21 | # TODO strip whitespace and comments from asm 22 | if result.strip() != data['asm'].strip(): 23 | diff = difflib.unified_diff(data['asm'].splitlines(), result.splitlines(), lineterm="") 24 | formatted = ''.join(' %s\n' % x for x in diff) 25 | raise AssertionError("Assembly differs:\n%s" % formatted) 26 | 27 | def test_datafiles(): 28 | # Nose test generator 29 | # Creates a testcase for each datafile 30 | for filename in testdata.list_files(): 31 | yield check_datafile, filename 32 | -------------------------------------------------------------------------------- /test_framework/test_jit.py: -------------------------------------------------------------------------------- 1 | import os 2 | import platform 3 | import tempfile 4 | import struct 5 | import re 6 | from subprocess import Popen, PIPE 7 | from nose.plugins.skip import Skip, SkipTest 8 | import ubpf.assembler 9 | import testdata 10 | VM = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..", "vm", "test") 11 | try: 12 | xrange 13 | except NameError: 14 | xrange = range 15 | 16 | def jit_supported_platform(): 17 | """Is the JIT supported on the current platform.""" 18 | return platform.machine() in ['amd64', 'x86_64', 'arm64', 'aarch64'] 19 | 20 | def check_datafile(filename): 21 | """ 22 | Given assembly source code and an expected result, run the eBPF program and 23 | verify that the result matches. Uses the JIT compiler. 24 | """ 25 | if not jit_supported_platform(): 26 | raise SkipTest("JIT is not supported on the current platform") 27 | 28 | data = testdata.read(filename) 29 | if 'asm' not in data and 'raw' not in data: 30 | raise SkipTest("no asm or raw section in datafile") 31 | if 'result' not in data and 'error' not in data and 'error pattern' not in data: 32 | raise SkipTest("no result or error section in datafile") 33 | if not os.path.exists(VM): 34 | raise SkipTest("VM not found") 35 | if 'no jit' in data: 36 | raise SkipTest("JIT disabled for this testcase (%s)" % data['no jit']) 37 | 38 | if 'raw' in data: 39 | code = b''.join(struct.pack("=Q", x) for x in data['raw']) 40 | else: 41 | code = ubpf.assembler.assemble(data['asm']) 42 | 43 | memfile = None 44 | 45 | if 'mem' in data: 46 | memfile = tempfile.NamedTemporaryFile() 47 | memfile.write(data['mem']) 48 | memfile.flush() 49 | 50 | num_register_offsets = 20 51 | if 'no register offset' in data: 52 | # The JIT relies on a fixed register mapping for the call instruction 53 | num_register_offsets = 1 54 | 55 | try: 56 | for register_offset in xrange(0, num_register_offsets): 57 | cmd = [VM] 58 | if memfile: 59 | cmd.extend(['-m', memfile.name]) 60 | if 'reload' in data: 61 | cmd.extend(['-R']) 62 | if 'unload' in data: 63 | cmd.extend(['-U']) 64 | cmd.extend(['-j', '-r', str(register_offset), '-']) 65 | 66 | vm = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) 67 | 68 | stdout, stderr = vm.communicate(code) 69 | stdout = stdout.decode("utf-8") 70 | stderr = stderr.decode("utf-8") 71 | stderr = stderr.strip() 72 | 73 | if 'error' in data: 74 | if data['error'] != stderr: 75 | raise AssertionError("Expected error %r, got %r" % (data['error'], stderr)) 76 | elif 'error pattern' in data: 77 | if not re.search(data['error pattern'], stderr): 78 | raise AssertionError("Expected error matching %r, got %r" % (data['error pattern'], stderr)) 79 | else: 80 | if stderr: 81 | raise AssertionError("Unexpected error %r" % stderr) 82 | 83 | if 'result' in data: 84 | if vm.returncode != 0: 85 | raise AssertionError("VM exited with status %d, stderr=%r" % (vm.returncode, stderr)) 86 | expected = int(data['result'], 0) 87 | result = int(stdout, 0) 88 | if expected != result: 89 | raise AssertionError("Expected result 0x%x, got 0x%x, stderr=%r" % (expected, result, stderr)) 90 | else: 91 | if vm.returncode == 0: 92 | raise AssertionError("Expected VM to exit with an error code") 93 | finally: 94 | if memfile: 95 | memfile.close() 96 | 97 | def test_datafiles(): 98 | # Nose test generator 99 | # Creates a testcase for each datafile 100 | for filename in testdata.list_files(): 101 | yield check_datafile, filename 102 | -------------------------------------------------------------------------------- /test_framework/test_roundtrip.py: -------------------------------------------------------------------------------- 1 | import struct 2 | import difflib 3 | from nose.plugins.skip import Skip, SkipTest 4 | import ubpf.assembler 5 | import ubpf.disassembler 6 | import testdata 7 | 8 | # Just for assertion messages 9 | def try_disassemble(inst): 10 | data = struct.pack("=Q", inst) 11 | try: 12 | return ubpf.disassembler.disassemble(data).strip() 13 | except ValueError: 14 | return "" 15 | 16 | def check_datafile(filename): 17 | """ 18 | Verify that the reassembling the output of the disassembler produces 19 | the same binary, and that disassembling the output of the assembler 20 | produces the same text. 21 | """ 22 | data = testdata.read(filename) 23 | 24 | if 'asm' not in data: 25 | raise SkipTest("no asm section in datafile") 26 | 27 | assembled = ubpf.assembler.assemble(data['asm']) 28 | disassembled = ubpf.disassembler.disassemble(assembled) 29 | reassembled = ubpf.assembler.assemble(disassembled) 30 | disassembled2 = ubpf.disassembler.disassemble(reassembled) 31 | 32 | if disassembled != disassembled2: 33 | diff = difflib.unified_diff(disassembled.splitlines(), disassembled2.splitlines(), lineterm="") 34 | formatted = ''.join(' %s\n' % x for x in diff) 35 | raise AssertionError("Assembly differs:\n%s" % formatted) 36 | 37 | if assembled != reassembled: 38 | raise AssertionError("binary differs") 39 | 40 | def test_datafiles(): 41 | # Nose test generator 42 | # Creates a testcase for each datafile 43 | for filename in testdata.list_files(): 44 | yield check_datafile, filename 45 | -------------------------------------------------------------------------------- /test_framework/test_vm.py: -------------------------------------------------------------------------------- 1 | import os 2 | import tempfile 3 | import struct 4 | import re 5 | from subprocess import Popen, PIPE 6 | from nose.plugins.skip import Skip, SkipTest 7 | import ubpf.assembler 8 | import testdata 9 | VM = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..", "vm", "test") 10 | 11 | def check_datafile(filename): 12 | """ 13 | Given assembly source code and an expected result, run the eBPF program and 14 | verify that the result matches. 15 | """ 16 | data = testdata.read(filename) 17 | if 'asm' not in data and 'raw' not in data: 18 | raise SkipTest("no asm or raw section in datafile") 19 | if 'result' not in data and 'error' not in data and 'error pattern' not in data: 20 | raise SkipTest("no result or error section in datafile") 21 | if not os.path.exists(VM): 22 | raise SkipTest("VM not found") 23 | 24 | if 'raw' in data: 25 | code = b''.join(struct.pack("=Q", x) for x in data['raw']) 26 | else: 27 | code = ubpf.assembler.assemble(data['asm']) 28 | 29 | memfile = None 30 | 31 | cmd = [VM] 32 | if 'mem' in data: 33 | memfile = tempfile.NamedTemporaryFile() 34 | memfile.write(data['mem']) 35 | memfile.flush() 36 | cmd.extend(['-m', memfile.name]) 37 | if 'reload' in data: 38 | cmd.extend(['-R']) 39 | if 'unload' in data: 40 | cmd.extend(['-U']) 41 | 42 | cmd.append('-') 43 | 44 | vm = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) 45 | 46 | stdout, stderr = vm.communicate(code) 47 | stdout = stdout.decode("utf-8") 48 | stderr = stderr.decode("utf-8") 49 | stderr = stderr.strip() 50 | 51 | if memfile: 52 | memfile.close() 53 | 54 | if 'error' in data: 55 | if data['error'] != stderr: 56 | raise AssertionError("Expected error %r, got %r" % (data['error'], stderr)) 57 | elif 'error pattern' in data: 58 | if not re.search(data['error pattern'], stderr): 59 | raise AssertionError("Expected error matching %r, got %r" % (data['error pattern'], stderr)) 60 | else: 61 | if stderr: 62 | raise AssertionError("Unexpected error %r" % stderr) 63 | 64 | if 'result' in data: 65 | if vm.returncode != 0: 66 | raise AssertionError("VM exited with status %d, stderr=%r" % (vm.returncode, stderr)) 67 | expected = int(data['result'], 0) 68 | result = int(stdout, 0) 69 | if expected != result: 70 | raise AssertionError("Expected result 0x%x, got 0x%x, stderr=%r" % (expected, result, stderr)) 71 | else: 72 | if vm.returncode == 0: 73 | raise AssertionError("Expected VM to exit with an error code") 74 | 75 | def test_datafiles(): 76 | # Nose test generator 77 | # Creates a testcase for each datafile 78 | for filename in testdata.list_files(): 79 | yield check_datafile, filename 80 | -------------------------------------------------------------------------------- /test_framework/testdata.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | 4 | _test_data_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "../tests") 5 | 6 | def list_files(): 7 | """ 8 | Return a list of data files under tests/data 9 | 10 | These strings are suitable to be passed to read(). 11 | """ 12 | 13 | result = [] 14 | for dirname, dirnames, filenames in os.walk(_test_data_dir): 15 | dirname = (os.path.relpath(dirname, _test_data_dir) + '/').replace('./', '') 16 | for filename in filenames: 17 | if filename.endswith('.data') and not filename.startswith('.'): 18 | result.append(dirname + filename) 19 | return sorted(result) 20 | 21 | def read(name): 22 | """ 23 | Read, parse, and return a test data file 24 | 25 | @param name Filename relative to the tests/data directory 26 | @returns A hash from section to the string contents 27 | """ 28 | 29 | section_lines = {} 30 | cur_section = None 31 | 32 | with open(os.path.join(_test_data_dir, name)) as f: 33 | for line in f: 34 | line = line.rstrip().partition('#')[0].rstrip() 35 | if line == '': 36 | continue 37 | elif line.startswith('--'): 38 | cur_section = line[2:].strip() 39 | if cur_section in section_lines: 40 | raise Exception("section %s already exists in the test data file") 41 | section_lines[cur_section] = [] 42 | elif cur_section: 43 | section_lines[cur_section].append(line) 44 | data = { section: '\n'.join(lines) for (section, lines) in list(section_lines.items()) } 45 | 46 | 47 | # Resolve links 48 | for k in list(data): 49 | if '@' in k: 50 | del data[k] 51 | section, path = k.split('@') 52 | section = section.strip() 53 | path = path.strip() 54 | fullpath = os.path.join(_test_data_dir, os.path.dirname(name), path) 55 | with open(fullpath) as f: 56 | data[section] = f.read() 57 | 58 | # Special case: convert 'raw' section into binary 59 | # Each line is parsed as an integer representing an instruction. 60 | if 'raw' in data: 61 | insts = [] 62 | for line in data['raw'].splitlines(): 63 | num, _, _ = line.partition("#") 64 | insts.append(int(num, 0)) 65 | data['raw'] = insts 66 | # 67 | # Special case: convert 'mem' section into binary 68 | # The string '00 11\n22 33' results in "\x00\x11\x22\x33" 69 | # Ignores hexdump prefix ending with a colon. 70 | if 'mem' in data: 71 | hex_strs = [] 72 | for line in data['mem'].splitlines(): 73 | if ':' in line: 74 | line = line[(line.rindex(':')+1):] 75 | hex_strs.extend(re.findall(r"[0-9A-Fa-f]{2}", line)) 76 | data['mem'] = bytes(bytearray([(int(x, 16)) for x in hex_strs])) 77 | 78 | return data 79 | -------------------------------------------------------------------------------- /tests/add.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 %r0, 0 3 | mov32 %r1, 2 4 | add32 %r0, 1 5 | add32 %r0, %r1 6 | exit 7 | -- result 8 | 0x3 9 | -------------------------------------------------------------------------------- /tests/add64.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r0, 1 3 | add %r0, -1 4 | exit 5 | -- result 6 | 0x0 7 | -------------------------------------------------------------------------------- /tests/alu-arith.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 %r0, 0 3 | mov32 %r1, 1 4 | mov32 %r2, 2 5 | mov32 %r3, 3 6 | mov32 %r4, 4 7 | mov32 %r5, 5 8 | mov32 %r6, 6 9 | mov32 %r7, 7 10 | mov32 %r8, 8 11 | mov32 %r9, 9 12 | # %r0 == 0 13 | 14 | add32 %r0, 23 15 | add32 %r0, %r7 16 | # %r0 == 30 17 | 18 | sub32 %r0, 13 19 | sub32 %r0, %r1 20 | # %r0 == 16 21 | 22 | mul32 %r0, 7 23 | mul32 %r0, %r3 24 | # %r0 == 336 25 | 26 | div32 %r0, 2 27 | div32 %r0, %r4 28 | # %r0 == 42 29 | 30 | exit 31 | -- result 32 | 0x2a 33 | -------------------------------------------------------------------------------- /tests/alu-bit.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 %r0, 0 3 | mov32 %r1, 1 4 | mov32 %r2, 2 5 | mov32 %r3, 3 6 | mov32 %r4, 4 7 | mov32 %r5, 5 8 | mov32 %r6, 6 9 | mov32 %r7, 7 10 | mov32 %r8, 8 11 | # %r0 == 0 12 | 13 | or32 %r0, %r5 14 | or32 %r0, 0xa0 15 | # %r0 == 0xa5 16 | 17 | and32 %r0, 0xa3 18 | mov32 %r9, 0x91 19 | and32 %r0, %r9 20 | # %r0 == 0x21 21 | 22 | lsh32 %r0, 22 23 | lsh32 %r0, %r8 24 | # %r0 == 0x40000000 25 | 26 | rsh32 %r0, 19 27 | rsh32 %r0, %r7 28 | # %r0 == 0x10 29 | 30 | xor32 %r0, 0x03 31 | xor32 %r0, %r2 32 | # %r0 == 0x11 33 | 34 | exit 35 | -- result 36 | 0x11 37 | -------------------------------------------------------------------------------- /tests/alu.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | add32 %r1, 0x2 3 | add32 %r9, 0xffffffff 4 | add32 %r1, %r2 5 | sub32 %r1, %r2 6 | mul32 %r1, %r2 7 | div32 %r1, %r2 8 | or32 %r1, %r2 9 | and32 %r1, %r2 10 | lsh32 %r1, %r2 11 | rsh32 %r1, %r2 12 | neg32 %r1 13 | mod32 %r1, %r2 14 | xor32 %r1, %r2 15 | mov32 %r1, %r2 16 | arsh32 %r1, %r2 17 | le16 %r1 18 | le32 %r1 19 | le64 %r1 20 | be16 %r1 21 | be32 %r1 22 | be64 %r1 23 | -- raw 24 | 0x0000000200000104 25 | 0xffffffff00000904 26 | 0x000000000000210c 27 | 0x000000000000211c 28 | 0x000000000000212c 29 | 0x000000000000213c 30 | 0x000000000000214c 31 | 0x000000000000215c 32 | 0x000000000000216c 33 | 0x000000000000217c 34 | 0x0000000000000184 35 | 0x000000000000219c 36 | 0x00000000000021ac 37 | 0x00000000000021bc 38 | 0x00000000000021cc 39 | 0x00000010000001d4 40 | 0x00000020000001d4 41 | 0x00000040000001d4 42 | 0x00000010000001dc 43 | 0x00000020000001dc 44 | 0x00000040000001dc 45 | -------------------------------------------------------------------------------- /tests/alu64-arith.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r0, 0 3 | mov %r1, 1 4 | mov %r2, 2 5 | mov %r3, 3 6 | mov %r4, 4 7 | mov %r5, 5 8 | mov %r6, 6 9 | mov %r7, 7 10 | mov %r8, 8 11 | mov %r9, 9 12 | # %r0 == 0 13 | 14 | add %r0, 23 15 | add %r0, %r7 16 | # %r0 == 30 17 | 18 | sub %r0, 13 19 | sub %r0, %r1 20 | # %r0 == 16 21 | 22 | mul %r0, 7 23 | mul %r0, %r3 24 | # %r0 == 336 25 | 26 | div %r0, 2 27 | div %r0, %r4 28 | # %r0 == 42 29 | 30 | exit 31 | -- result 32 | 0x2a 33 | -------------------------------------------------------------------------------- /tests/alu64-bit.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r0, 0 3 | mov %r1, 1 4 | mov %r2, 2 5 | mov %r3, 3 6 | mov %r4, 4 7 | mov %r5, 5 8 | mov %r6, 6 9 | mov %r7, 7 10 | mov %r8, 8 11 | # %r0 == 0 12 | 13 | or %r0, %r5 14 | or %r0, 0xa0 15 | # %r0 == 0xa5 16 | 17 | and %r0, 0xa3 18 | mov %r9, 0x91 19 | and %r0, %r9 20 | # %r0 == 0x21 21 | 22 | lsh %r0, 32 23 | lsh %r0, 22 24 | lsh %r0, %r8 25 | # %r0 == 0x40000000 26 | 27 | rsh %r0, 32 28 | rsh %r0, 19 29 | rsh %r0, %r7 30 | # %r0 == 0x10 31 | 32 | xor %r0, 0x03 33 | xor %r0, %r2 34 | # %r0 == 0x11 35 | 36 | exit 37 | -- result 38 | 0x11 39 | -------------------------------------------------------------------------------- /tests/alu64.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | add %r1, 0x2 3 | add %r9, 0xffffffff 4 | add %r1, %r2 5 | sub %r1, %r2 6 | mul %r1, %r2 7 | div %r1, %r2 8 | or %r1, %r2 9 | and %r1, %r2 10 | lsh %r1, %r2 11 | rsh %r1, %r2 12 | neg %r1 13 | mod %r1, %r2 14 | xor %r1, %r2 15 | mov %r1, %r2 16 | arsh %r1, %r2 17 | -- raw 18 | 0x0000000200000107 19 | 0xffffffff00000907 20 | 0x000000000000210f 21 | 0x000000000000211f 22 | 0x000000000000212f 23 | 0x000000000000213f 24 | 0x000000000000214f 25 | 0x000000000000215f 26 | 0x000000000000216f 27 | 0x000000000000217f 28 | 0x0000000000000187 29 | 0x000000000000219f 30 | 0x00000000000021af 31 | 0x00000000000021bf 32 | 0x00000000000021cf 33 | -------------------------------------------------------------------------------- /tests/arsh-reg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 %r0, 0xf8 3 | mov32 %r1, 16 4 | lsh32 %r0, 28 5 | # %r0 == 0x80000000 6 | arsh32 %r0, %r1 7 | exit 8 | -- result 9 | 0xffff8000 10 | -------------------------------------------------------------------------------- /tests/arsh.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 %r0, 0xf8 3 | lsh32 %r0, 28 4 | # %r0 == 0x80000000 5 | arsh32 %r0, 16 6 | exit 7 | -- result 8 | 0xffff8000 9 | 10 | -------------------------------------------------------------------------------- /tests/arsh32-high-shift.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r0, 8 3 | lddw %r1, 0x100000001 4 | arsh32 %r0, %r1 5 | exit 6 | -- result 7 | 0x4 8 | -------------------------------------------------------------------------------- /tests/arsh64.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 %r0, 1 3 | lsh %r0, 63 4 | arsh %r0, 55 5 | mov32 %r1, 5 6 | arsh %r0, %r1 7 | exit 8 | -- result 9 | 0xfffffffffffffff8 10 | -------------------------------------------------------------------------------- /tests/be16-high.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | ldxdw %r0, [%r1] 3 | be16 %r0 4 | exit 5 | -- mem 6 | 11 22 33 44 55 66 77 88 7 | -- result 8 | 0x1122 9 | -------------------------------------------------------------------------------- /tests/be16.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | ldxh %r0, [%r1] 3 | be16 %r0 4 | exit 5 | -- mem 6 | 11 22 7 | -- result 8 | 0x1122 9 | -------------------------------------------------------------------------------- /tests/be32-high.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | ldxdw %r0, [%r1] 3 | be32 %r0 4 | exit 5 | -- mem 6 | 11 22 33 44 55 66 77 88 7 | -- result 8 | 0x11223344 9 | -------------------------------------------------------------------------------- /tests/be32.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | ldxw %r0, [%r1] 3 | be32 %r0 4 | exit 5 | -- mem 6 | 11 22 33 44 7 | -- result 8 | 0x11223344 9 | -------------------------------------------------------------------------------- /tests/be64.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | ldxdw %r0, [%r1] 3 | be64 %r0 4 | exit 5 | -- mem 6 | 11 22 33 44 55 66 77 88 7 | -- result 8 | 0x1122334455667788 9 | -------------------------------------------------------------------------------- /tests/call-memfrob.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r6, %r1 3 | add %r1, 2 4 | mov %r2, 4 5 | call 1 6 | ldxdw %r0, [%r6] 7 | be64 %r0 8 | exit 9 | -- mem 10 | 01 02 03 04 05 06 07 08 11 | -- result 12 | 0x102292e2f2c0708 13 | -- no register offset 14 | call instruction 15 | -------------------------------------------------------------------------------- /tests/call-save.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r6, 0x0001 3 | mov %r7, 0x0020 4 | mov %r8, 0x0300 5 | mov %r9, 0x4000 6 | 7 | # r1 should contain pointer to program memory. 8 | # Don't screw that up because helper function 1 (memfrob) 9 | # needs it. 10 | mov %r2, 0x0001 11 | mov %r3, 0x0001 12 | mov %r4, 0x0001 13 | mov %r5, 0x0001 14 | call 1 15 | mov %r0, 0 16 | or %r0, %r6 17 | or %r0, %r7 18 | or %r0, %r8 19 | or %r0, %r9 20 | jeq %r0, 0x4321, +1 21 | exit 22 | 23 | # Call helper function 0 -- the memory pointer is 24 | # no longer needed for any other helper functions, so 25 | # we don't have to worry about keeping it safe. 26 | mov %r1, 0x0001 27 | mov %r2, 0x0001 28 | mov %r3, 0x0001 29 | mov %r4, 0x0001 30 | mov %r5, 0x0001 31 | call 0 32 | mov %r0, 0 33 | or %r0, %r6 34 | or %r0, %r7 35 | or %r0, %r8 36 | or %r0, %r9 37 | jeq %r0, 0x4321, +1 38 | exit 39 | 40 | mov %r1, 0x0001 41 | mov %r2, 0x0001 42 | mov %r3, 0x0001 43 | mov %r4, 0x0001 44 | mov %r5, 0x0001 45 | call 2 46 | mov %r0, 0 47 | or %r0, %r6 48 | or %r0, %r7 49 | or %r0, %r8 50 | or %r0, %r9 51 | jeq %r0, 0x4321, +1 52 | exit 53 | 54 | mov %r1, 0x0001 55 | mov %r2, 0x0001 56 | mov %r3, 0x0001 57 | mov %r4, 0x0001 58 | mov %r5, 0x0001 59 | call 3 60 | mov %r0, 0 61 | or %r0, %r6 62 | or %r0, %r7 63 | or %r0, %r8 64 | or %r0, %r9 65 | exit 66 | -- mem 67 | 01 02 03 04 05 06 07 08 68 | -- result 69 | 0x4321 70 | -- no register offset 71 | call instruction 72 | -------------------------------------------------------------------------------- /tests/call.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r1, 1 3 | mov %r2, 2 4 | mov %r3, 3 5 | mov %r4, 4 6 | mov %r5, 5 7 | call 0 8 | exit 9 | -- result 10 | 0x0102030405 11 | -- no register offset 12 | call instruction 13 | -------------------------------------------------------------------------------- /tests/call_local_use_stack.data: -------------------------------------------------------------------------------- 1 | # Copyright (c) Will Hawkins 2 | # SPDX-License-Identifier: Apache-2.0 3 | -- asm 4 | mov %r0, 0x55 5 | # Move 0x55 onto the stack (right at the top). 6 | stxw [%r10-4], %r0 7 | call local func1 8 | # As long as there is local stack space allocated 9 | # by the runtime for local functions, then the value 10 | # stored on the stack in func1 will not have clobbered 11 | # this value. 12 | ldxw %r0, [%r10-4] 13 | exit 14 | 15 | func1: 16 | # If the stack pointer is not properly adjusted to make space 17 | # for local variables for this local function, then this write 18 | # will clobber what "main" put on the stack and will, ultimately, 19 | # set as the return value. 20 | mov %r0, 0x66 21 | stxw [%r10-4], %r0 22 | exit 23 | 24 | -- result 25 | 0x55 26 | -------------------------------------------------------------------------------- /tests/call_unwind.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r1, 0 3 | call 5 4 | mov %r0, 2 5 | exit 6 | -- result 7 | 0x0 8 | -- no register offset 9 | call instruction 10 | -------------------------------------------------------------------------------- /tests/call_unwind_fail.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r1, -1 3 | call 5 4 | mov %r0, 2 5 | exit 6 | -- result 7 | 0x2 8 | -- no register offset 9 | call instruction 10 | -------------------------------------------------------------------------------- /tests/div-by-zero-imm.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 %r0, 1 3 | div32 %r0, 0 4 | exit 5 | -- result 6 | 0x0 7 | -------------------------------------------------------------------------------- /tests/div-by-zero-reg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 %r0, 1 3 | mov32 %r1, 0 4 | div32 %r0, %r1 5 | exit 6 | -- result 7 | 0x0 8 | -------------------------------------------------------------------------------- /tests/div32-by-zero-reg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 %r0, 1 3 | lddw %r1, 0x100000000 4 | div32 %r0, %r1 5 | exit 6 | -- result 7 | 0x0 8 | -------------------------------------------------------------------------------- /tests/div32-high-divisor.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r0, 12 3 | lddw %r1, 0x100000004 4 | div32 %r0, %r1 5 | exit 6 | -- result 7 | 0x3 8 | -------------------------------------------------------------------------------- /tests/div32-imm.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | lddw %r0, 0x10000000c 3 | div32 %r0, 4 4 | exit 5 | -- result 6 | 0x3 7 | -------------------------------------------------------------------------------- /tests/div32-reg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | lddw %r0, 0x10000000c 3 | mov %r1, 4 4 | div32 %r0, %r1 5 | exit 6 | -- result 7 | 0x3 8 | -------------------------------------------------------------------------------- /tests/div64-by-zero-imm.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 %r0, 1 3 | div %r0, 0 4 | exit 5 | -- result 6 | 0x0 7 | -------------------------------------------------------------------------------- /tests/div64-by-zero-reg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 %r0, 1 3 | mov32 %r1, 0 4 | div %r0, %r1 5 | exit 6 | -- result 7 | 0x0 8 | -------------------------------------------------------------------------------- /tests/div64-imm.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r0, 0xc 3 | lsh %r0, 32 4 | div %r0, 4 5 | exit 6 | -- result 7 | 0x300000000 8 | -------------------------------------------------------------------------------- /tests/div64-negative-imm.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | lddw %r0, 0xFFFFFFFFFFFFFFFF 3 | div %r0, -10 4 | exit 5 | -- result 6 | 0x1 7 | -------------------------------------------------------------------------------- /tests/div64-negative-reg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | lddw %r0, 0xFFFFFFFFFFFFFFFF 3 | mov32 %r1, -10 4 | div %r0, %r1 5 | exit 6 | -- result 7 | 0x10000000A 8 | -------------------------------------------------------------------------------- /tests/div64-reg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r0, 0xc 3 | lsh %r0, 32 4 | mov %r1, 4 5 | div %r0, %r1 6 | exit 7 | -- result 8 | 0x300000000 9 | -------------------------------------------------------------------------------- /tests/early-exit.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r0, 3 3 | exit 4 | mov %r0, 4 5 | exit 6 | -- result 7 | 0x3 8 | -------------------------------------------------------------------------------- /tests/elf/bad-rel-offset.data: -------------------------------------------------------------------------------- 1 | -- pyelf 2 | sqrti_rel.r_offset = 500 3 | -- error 4 | Failed to load code: bad relocation offset 5 | -------------------------------------------------------------------------------- /tests/elf/bad-rel-strtab-index.data: -------------------------------------------------------------------------------- 1 | -- pyelf 2 | symtab_shdr.sh_link = 30 3 | -- error 4 | Failed to load code: bad string table section index 5 | -------------------------------------------------------------------------------- /tests/elf/bad-rel-symbol-index.data: -------------------------------------------------------------------------------- 1 | -- pyelf 2 | sqrti_rel.r_info = (10 << 32) | 2 3 | -- error 4 | Failed to load code: bad symbol index 5 | -------------------------------------------------------------------------------- /tests/elf/bad-rel-symbol-name.data: -------------------------------------------------------------------------------- 1 | -- pyelf 2 | sqrti_sym.st_name = 100 3 | -- error 4 | Failed to load code: bad symbol name 5 | -------------------------------------------------------------------------------- /tests/elf/bad-rel-symbol-table-section-index.data: -------------------------------------------------------------------------------- 1 | -- pyelf 2 | rel_shdr.sh_link = 30 3 | -- error 4 | Failed to load code: bad symbol table section index 5 | -------------------------------------------------------------------------------- /tests/elf/bad-rel-type.data: -------------------------------------------------------------------------------- 1 | -- pyelf 2 | sqrti_rel.r_info = (1 << 32) | 3 3 | -- error 4 | Failed to load code: bad relocation type 3 5 | -------------------------------------------------------------------------------- /tests/elf/bad-section-header-offset.data: -------------------------------------------------------------------------------- 1 | -- pyelf 2 | ehdr.e_shoff = 1024 3 | -- error 4 | Failed to load code: bad section header offset or size 5 | -------------------------------------------------------------------------------- /tests/elf/bad-section-header-size.data: -------------------------------------------------------------------------------- 1 | -- pyelf 2 | ehdr.e_shentsize = 1024 3 | -- error 4 | Failed to load code: bad section header offset or size 5 | -------------------------------------------------------------------------------- /tests/elf/bad-section-offset.data: -------------------------------------------------------------------------------- 1 | -- pyelf 2 | text_shdr.sh_offset = 1024 3 | -- error 4 | Failed to load code: bad section offset or size 5 | -------------------------------------------------------------------------------- /tests/elf/bad-section-size.data: -------------------------------------------------------------------------------- 1 | -- pyelf 2 | text_shdr.sh_size = 1024 3 | -- error 4 | Failed to load code: bad section offset or size 5 | -------------------------------------------------------------------------------- /tests/elf/ehdr-short.data: -------------------------------------------------------------------------------- 1 | -- pyelf 2 | del order[:] 3 | magic = b'\x7fELF' 4 | order.append('magic') 5 | -- error 6 | Failed to load code: not enough data for ELF header 7 | -------------------------------------------------------------------------------- /tests/elf/no-text-section.data: -------------------------------------------------------------------------------- 1 | -- pyelf 2 | text_shdr.sh_type = 'SHT_NULL' 3 | -- error 4 | Failed to load code: text section not found 5 | -------------------------------------------------------------------------------- /tests/elf/ok.data: -------------------------------------------------------------------------------- 1 | -- pyelf 2 | -- result 3 | 0x2a 4 | -------------------------------------------------------------------------------- /tests/elf/rel-sym-not-found.data: -------------------------------------------------------------------------------- 1 | -- pyelf 2 | sqrti_sym.st_name += 1 3 | -- error 4 | Failed to load code: function 'qrti' not found 5 | -------------------------------------------------------------------------------- /tests/elf/too-many-sections.data: -------------------------------------------------------------------------------- 1 | -- pyelf 2 | ehdr.e_shnum = 1024 3 | -- error 4 | Failed to load code: too many sections 5 | -------------------------------------------------------------------------------- /tests/elf/wrong-byte-order.data: -------------------------------------------------------------------------------- 1 | -- pyelf 2 | ehdr.e_ident.EI_DATA = 'ELFDATA2MSB' 3 | -- error 4 | Failed to load code: wrong byte order 5 | -------------------------------------------------------------------------------- /tests/elf/wrong-class.data: -------------------------------------------------------------------------------- 1 | -- pyelf 2 | ehdr.e_ident.EI_CLASS = 'ELFCLASS32' 3 | -- error 4 | Failed to load code: wrong class 5 | -------------------------------------------------------------------------------- /tests/elf/wrong-machine.data: -------------------------------------------------------------------------------- 1 | -- pyelf 2 | ehdr.e_machine = 1 3 | -- error 4 | Failed to load code: wrong machine, expected none or BPF, got 1 5 | -------------------------------------------------------------------------------- /tests/elf/wrong-osabi.data: -------------------------------------------------------------------------------- 1 | -- pyelf 2 | ehdr.e_ident.EI_OSABI = 'ELFOSABI_LINUX' 3 | -- error 4 | Failed to load code: wrong OS ABI 5 | -------------------------------------------------------------------------------- /tests/elf/wrong-type.data: -------------------------------------------------------------------------------- 1 | -- pyelf 2 | ehdr.e_type = 0 3 | -- error 4 | Failed to load code: wrong type, expected relocatable 5 | -------------------------------------------------------------------------------- /tests/elf/wrong-version.data: -------------------------------------------------------------------------------- 1 | -- pyelf 2 | ehdr.e_ident.EI_VERSION = 2 3 | -- error 4 | Failed to load code: wrong version 5 | -------------------------------------------------------------------------------- /tests/err-call-bad-imm.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r1, 1 3 | mov %r2, 2 4 | mov %r3, 3 5 | mov %r4, 4 6 | mov %r5, 5 7 | call 64 8 | exit 9 | -- error 10 | Failed to load code: call to nonexistent function 64 at PC 5 11 | -------------------------------------------------------------------------------- /tests/err-call-invalid-jump.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | begin: 3 | mov %r1, 0x12345678 4 | call local next 5 | exit 6 | next: 7 | add %r1, 1 8 | ja begin # Intentionally jump from the sub-program back to the main program. 9 | exit 10 | -- result 11 | 0x12345678 12 | -- errror 13 | Failed to load code: jump out of bounds at PC 4 14 | -------------------------------------------------------------------------------- /tests/err-call-unreg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r1, 1 3 | mov %r2, 2 4 | mov %r3, 3 5 | mov %r4, 4 6 | mov %r5, 5 7 | call 63 8 | exit 9 | -- error 10 | Failed to load code: call to nonexistent function 63 at PC 5 11 | -------------------------------------------------------------------------------- /tests/err-call0.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r0, 0x12345678 3 | call local next 4 | next: 5 | exit 6 | -- result 7 | 0x12345678 8 | -- error 9 | Failed to load code: sub-program does not end with EXIT at PC 1 10 | -------------------------------------------------------------------------------- /tests/err-endian-size.data: -------------------------------------------------------------------------------- 1 | -- raw 2 | 0x00000030000001dc 3 | 0x00000000000010b7 4 | 0x0000000000000095 5 | -- error 6 | Failed to load code: invalid endian immediate at PC 0 7 | -------------------------------------------------------------------------------- /tests/err-incomplete-lddw.data: -------------------------------------------------------------------------------- 1 | -- raw 2 | 0x5566778800000018 3 | 0x0000000000000095 4 | -- error 5 | Failed to load code: incomplete lddw at PC 0 6 | -------------------------------------------------------------------------------- /tests/err-incomplete-lddw2.data: -------------------------------------------------------------------------------- 1 | -- raw 2 | 0x5566778800000018 3 | 0x0000000000000095 4 | -- error 5 | Failed to load code: incomplete lddw at PC 0 6 | -------------------------------------------------------------------------------- /tests/err-infinite-loop.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | ja -1 3 | exit 4 | -- error 5 | Failed to load code: infinite loop at PC 0 6 | -------------------------------------------------------------------------------- /tests/err-invalid-reg-dst.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r11, 1 3 | exit 4 | -- error 5 | Failed to load code: invalid destination register at PC 0 6 | -------------------------------------------------------------------------------- /tests/err-invalid-reg-src.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r0, %r11 3 | exit 4 | -- error 5 | Failed to load code: invalid source register at PC 0 6 | -------------------------------------------------------------------------------- /tests/err-jmp-lddw.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | ja +1 3 | lddw %r0, 0x1122334455667788 4 | exit 5 | -- error 6 | Failed to load code: jump to middle of lddw at PC 0 7 | -------------------------------------------------------------------------------- /tests/err-jmp-out.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | ja +2 3 | exit 4 | -- error 5 | Failed to load code: jump out of bounds at PC 0 6 | -------------------------------------------------------------------------------- /tests/err-lddw-invalid-src.data: -------------------------------------------------------------------------------- 1 | -- raw 2 | 0x5566778800001118 3 | 0x0000000000000000 4 | -- error 5 | Failed to load code: invalid source register for LDDW at PC 0 6 | -------------------------------------------------------------------------------- /tests/err-stack-oob.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | stb [%r10], 0 3 | exit 4 | -- error pattern 5 | uBPF error: out of bounds memory store at PC 0, addr .*, size 1 6 | -- result 7 | 0xffffffffffffffff 8 | -- no jit 9 | stack oob check not implemented 10 | -------------------------------------------------------------------------------- /tests/err-unknown-opcode.data: -------------------------------------------------------------------------------- 1 | -- raw 2 | 0x0000000000000006 3 | 0x0000000000000095 4 | -- error 5 | Failed to load code: unknown opcode 0x06 at PC 0 6 | -------------------------------------------------------------------------------- /tests/err-write-r10.dst: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r10, 1 3 | exit 4 | -- error 5 | Failed to load code: invalid destination register at PC 0 6 | -------------------------------------------------------------------------------- /tests/exit-not-last.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r1, 0 3 | ja +2 4 | mov %r2, 0 5 | exit 6 | mov %r0, 0 7 | ja -4 8 | -- result 9 | 0x0 10 | -------------------------------------------------------------------------------- /tests/exit.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r0, 0 3 | exit 4 | -- result 5 | 0x0 6 | -------------------------------------------------------------------------------- /tests/factorial.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r1, 1 3 | mov %r2, 20 4 | call local exponential 5 | exit 6 | exponential: 7 | mul %r1, %r2 8 | sub %r2, 1 9 | jne %r2, 0, exponential 10 | mov %r0, %r1 11 | exit 12 | -- result 13 | 0x21C3677C82B40000 14 | -------------------------------------------------------------------------------- /tests/ja.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r0, 1 3 | ja +1 4 | mov %r0, 2 5 | exit 6 | -- result 7 | 0x1 8 | -------------------------------------------------------------------------------- /tests/jeq-imm.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 %r0, 0 3 | mov32 %r1, 0xa 4 | jeq %r1, 0xb, +4 # Not taken 5 | 6 | mov32 %r0, 1 7 | mov32 %r1, 0xb 8 | jeq %r1, 0xb, +1 # Taken 9 | 10 | mov32 %r0, 2 # Skipped 11 | exit 12 | -- result 13 | 0x1 14 | -------------------------------------------------------------------------------- /tests/jeq-reg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 %r0, 0 3 | mov32 %r1, 0xa 4 | mov32 %r2, 0xb 5 | jeq %r1, %r2, +4 # Not taken 6 | 7 | mov32 %r0, 1 8 | mov32 %r1, 0xb 9 | jeq %r1, %r2, +1 # Taken 10 | 11 | mov32 %r0, 2 # Skipped 12 | exit 13 | -- result 14 | 0x1 15 | -------------------------------------------------------------------------------- /tests/jge-imm.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 %r0, 0 3 | mov32 %r1, 0xa 4 | jge %r1, 0xb, +4 # Not taken 5 | 6 | mov32 %r0, 1 7 | mov32 %r1, 0xc 8 | jge %r1, 0xb, +1 # Taken 9 | 10 | mov32 %r0, 2 # Skipped 11 | 12 | exit 13 | -- result 14 | 0x1 15 | -------------------------------------------------------------------------------- /tests/jgt-imm.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 %r0, 0 3 | mov32 %r1, 5 4 | jgt %r1, 6, +2 # Not taken 5 | jgt %r1, 5, +1 # Not taken 6 | jgt %r1, 4, +1 # Taken 7 | exit 8 | mov32 %r0, 1 9 | exit 10 | -- result 11 | 0x1 12 | -------------------------------------------------------------------------------- /tests/jgt-reg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r0, 0 3 | mov %r1, 5 4 | mov %r2, 6 5 | mov %r3, 4 6 | jgt %r1, %r2, +2 # Not taken 7 | jgt %r1, %r1, +1 # Not taken 8 | jgt %r1, %r3, +1 # Taken 9 | exit 10 | mov %r0, 1 11 | exit 12 | -- result 13 | 0x1 14 | -------------------------------------------------------------------------------- /tests/jit-bounce.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r0, 1 3 | mov %r6, %r0 4 | mov %r7, %r6 5 | mov %r8, %r7 6 | mov %r9, %r8 7 | # TODO %r10 8 | mov %r0, %r9 9 | exit 10 | -- result 11 | 0x1 12 | -------------------------------------------------------------------------------- /tests/jle-imm.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 %r0, 0 3 | mov32 %r1, 5 4 | jle %r1, 4, +1 # Not taken 5 | jle %r1, 6, +1 # Taken 6 | exit 7 | jle %r1, 5, +1 # Taken 8 | exit 9 | mov32 %r0, 1 10 | exit 11 | -- result 12 | 0x1 13 | -------------------------------------------------------------------------------- /tests/jle-reg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r0, 0 3 | mov %r1, 5 4 | mov %r2, 4 5 | mov %r3, 6 6 | jle %r1, %r2, +2 # Not taken 7 | jle %r1, %r1, +1 # Taken 8 | exit 9 | jle %r1, %r3, +1 # Taken 10 | exit 11 | mov %r0, 1 12 | exit 13 | -- result 14 | 0x1 15 | -------------------------------------------------------------------------------- /tests/jlt-imm.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 %r0, 0 3 | mov32 %r1, 5 4 | jlt %r1, 4, +2 # Not taken 5 | jlt %r1, 5, +1 # Not taken 6 | jlt %r1, 6, +1 # Taken 7 | exit 8 | mov32 %r0, 1 9 | exit 10 | -- result 11 | 0x1 12 | -------------------------------------------------------------------------------- /tests/jlt-reg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r0, 0 3 | mov %r1, 5 4 | mov %r2, 4 5 | mov %r3, 6 6 | jlt %r1, %r2, +2 # Not taken 7 | jlt %r1, %r1, +1 # Not taken 8 | jlt %r1, %r3, +1 # Taken 9 | exit 10 | mov %r0, 1 11 | exit 12 | -- result 13 | 0x1 14 | -------------------------------------------------------------------------------- /tests/jmp.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | ja +1 3 | ja +32767 4 | ja -1 5 | ja -32768 6 | jeq %r1, 0x33, +1 7 | jeq %r1, %r2, +1 8 | jgt %r1, %r2, +1 9 | jge %r1, %r2, +1 10 | jset %r1, %r2, +1 11 | jne %r1, %r2, +1 12 | jsgt %r1, %r2, +1 13 | jsge %r1, %r2, +1 14 | call 0x1 15 | exit 16 | -- raw 17 | 0x0000000000010005 18 | 0x000000007fff0005 19 | 0x00000000ffff0005 20 | 0x0000000080000005 21 | 0x0000003300010115 22 | 0x000000000001211d 23 | 0x000000000001212d 24 | 0x000000000001213d 25 | 0x000000000001214d 26 | 0x000000000001215d 27 | 0x000000000001216d 28 | 0x000000000001217d 29 | 0x0000000100000085 30 | 0x0000000000000095 31 | -------------------------------------------------------------------------------- /tests/jne-reg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 %r0, 0 3 | mov32 %r1, 0xb 4 | mov32 %r2, 0xb 5 | jne %r1, %r2, +4 # Not taken 6 | 7 | mov32 %r0, 1 8 | mov32 %r1, 0xa 9 | jne %r1, %r2, +1 # Taken 10 | 11 | mov32 %r0, 2 # Skipped 12 | exit 13 | -- result 14 | 0x1 15 | -------------------------------------------------------------------------------- /tests/jset-imm.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 %r0, 0 3 | mov32 %r1, 0x7 4 | jset %r1, 0x8, +4 # Not taken 5 | 6 | mov32 %r0, 1 7 | mov32 %r1, 0x9 8 | jset %r1, 0x8, +1 # Taken 9 | 10 | mov32 %r0, 2 # Skipped 11 | 12 | exit 13 | -- result 14 | 0x1 15 | -------------------------------------------------------------------------------- /tests/jset-reg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 %r0, 0 3 | mov32 %r1, 0x7 4 | mov32 %r2, 0x8 5 | jset %r1, %r2, +4 # Not taken 6 | 7 | mov32 %r0, 1 8 | mov32 %r1, 0x9 9 | jset %r1, %r2, +1 # Taken 10 | 11 | mov32 %r0, 2 # Skipped 12 | 13 | exit 14 | -- result 15 | 0x1 16 | -------------------------------------------------------------------------------- /tests/jsge-imm.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 %r0, 0 3 | mov %r1, 0xfffffffe 4 | jsge %r1, 0xffffffff, +5 # Not taken 5 | jsge %r1, 0, +4 # Not taken 6 | 7 | mov32 %r0, 1 8 | mov %r1, 0xffffffff 9 | jsge %r1, 0xffffffff, +1 # Taken 10 | 11 | mov32 %r0, 2 # Skipped 12 | 13 | exit 14 | -- result 15 | 0x1 16 | -------------------------------------------------------------------------------- /tests/jsge-reg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 %r0, 0 3 | mov %r1, 0xfffffffe 4 | mov %r2, 0xffffffff 5 | mov32 %r3, 0 6 | jsge %r1, %r2, +5 # Not taken 7 | jsge %r1, %r3, +4 # Not taken 8 | 9 | mov32 %r0, 1 10 | mov %r1, %r2 11 | jsge %r1, %r2, +1 # Taken 12 | 13 | mov32 %r0, 2 # Skipped 14 | 15 | exit 16 | -- result 17 | 0x1 18 | -------------------------------------------------------------------------------- /tests/jsgt-imm.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 %r0, 0 3 | mov %r1, 0xfffffffe 4 | jsgt %r1, 0xffffffff, +4 # Not taken 5 | 6 | mov32 %r0, 1 7 | mov32 %r1, 0 8 | jsgt %r1, 0xffffffff, +1 # Taken 9 | 10 | mov32 %r0, 2 # Skipped 11 | 12 | exit 13 | -- result 14 | 0x1 15 | -------------------------------------------------------------------------------- /tests/jsgt-reg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 %r0, 0 3 | mov %r1, 0xfffffffe 4 | mov %r2, 0xffffffff 5 | jsgt %r1, %r2, +4 # Not taken 6 | 7 | mov32 %r0, 1 8 | mov32 %r1, 0 9 | jsgt %r1, %r2, +1 # Taken 10 | 11 | mov32 %r0, 2 # Skipped 12 | 13 | exit 14 | -- result 15 | 0x1 16 | -------------------------------------------------------------------------------- /tests/jsle-imm.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 %r0, 0 3 | mov %r1, 0xfffffffe 4 | jsle %r1, 0xfffffffd, +1 # Not taken 5 | jsle %r1, 0xffffffff, +1 # Taken 6 | exit 7 | mov32 %r0, 1 8 | jsle %r1, 0xfffffffe, +1 # Taken 9 | mov32 %r0, 2 10 | exit 11 | -- result 12 | 0x1 13 | -------------------------------------------------------------------------------- /tests/jsle-reg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 %r0, 0 3 | mov %r1, 0xffffffff 4 | mov %r2, 0xfffffffe 5 | mov32 %r3, 0 6 | jsle %r1, %r2, +1 # Not taken 7 | jsle %r1, %r3, +1 # Taken 8 | exit 9 | mov32 %r0, 1 10 | mov %r1, %r2 11 | jsle %r1, %r2, +1 # Taken 12 | mov32 %r0, 2 13 | exit 14 | -- result 15 | 0x1 16 | -------------------------------------------------------------------------------- /tests/jslt-imm.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 %r0, 0 3 | mov %r1, 0xfffffffe 4 | jslt %r1, 0xfffffffd, +2 # Not taken 5 | jslt %r1, 0xfffffffe, +1 # Not taken 6 | jslt %r1, 0xffffffff, +1 # Taken 7 | exit 8 | mov32 %r0, 1 9 | exit 10 | -- result 11 | 0x1 12 | -------------------------------------------------------------------------------- /tests/jslt-reg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 %r0, 0 3 | mov %r1, 0xfffffffe 4 | mov %r2, 0xfffffffd 5 | mov %r3, 0xffffffff 6 | jslt %r1, %r1, +2 # Not taken 7 | jslt %r1, %r2, +1 # Not taken 8 | jslt %r1, %r3, +1 # Taken 9 | exit 10 | mov32 %r0, 1 11 | exit 12 | -- result 13 | 0x1 14 | -------------------------------------------------------------------------------- /tests/lddw.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | lddw %r0, 0x1122334455667788 3 | exit 4 | -- raw 5 | 0x5566778800000018 6 | 0x1122334400000000 7 | 0x0000000000000095 8 | -- result 9 | 0x1122334455667788 10 | -------------------------------------------------------------------------------- /tests/lddw2.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | lddw %r0, 0x0000000080000000 3 | exit 4 | -- result 5 | 0x0000000080000000 6 | -------------------------------------------------------------------------------- /tests/ldx.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | ldxw %r1, [%r2] 3 | ldxh %r1, [%r2] 4 | ldxb %r1, [%r2] 5 | ldxdw %r1, [%r2] 6 | ldxw %r1, [%r2+1] 7 | ldxw %r1, [%r2+32767] 8 | ldxw %r1, [%r2-1] 9 | ldxw %r1, [%r2-32768] 10 | -- raw 11 | 0x0000000000002161 12 | 0x0000000000002169 13 | 0x0000000000002171 14 | 0x0000000000002179 15 | 0x0000000000012161 16 | 0x000000007fff2161 17 | 0x00000000ffff2161 18 | 0x0000000080002161 19 | -------------------------------------------------------------------------------- /tests/ldxb-0offset.data: -------------------------------------------------------------------------------- 1 | # Copyright (c) Big Switch Networks, Inc 2 | # SPDX-License-Identifier: Apache-2.0 3 | -- asm 4 | mov %r0, 0 5 | mov %r2, %r1 6 | mov %r7, %r1 7 | mov %r8, %r1 8 | add %r7, 1 9 | add %r8, 2 10 | ldxb %r0, [%r2+0] 11 | ldxb %r7, [%r7+0] 12 | ldxb %r8, [%r8+0] 13 | add %r0, %r7 14 | add %r0, %r8 15 | exit 16 | -- mem 17 | 01 02 03 18 | -- result 19 | 0x06 20 | -------------------------------------------------------------------------------- /tests/ldxb-all.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r0, %r1 3 | 4 | ldxb %r9, [%r0+0] 5 | lsh %r9, 0 6 | 7 | ldxb %r8, [%r0+1] 8 | lsh %r8, 4 9 | 10 | ldxb %r7, [%r0+2] 11 | lsh %r7, 8 12 | 13 | ldxb %r6, [%r0+3] 14 | lsh %r6, 12 15 | 16 | ldxb %r5, [%r0+4] 17 | lsh %r5, 16 18 | 19 | ldxb %r4, [%r0+5] 20 | lsh %r4, 20 21 | 22 | ldxb %r3, [%r0+6] 23 | lsh %r3, 24 24 | 25 | ldxb %r2, [%r0+7] 26 | lsh %r2, 28 27 | 28 | ldxb %r1, [%r0+8] 29 | lsh %r1, 32 30 | 31 | ldxb %r0, [%r0+9] 32 | lsh %r0, 36 33 | 34 | or %r0, %r1 35 | or %r0, %r2 36 | or %r0, %r3 37 | or %r0, %r4 38 | or %r0, %r5 39 | or %r0, %r6 40 | or %r0, %r7 41 | or %r0, %r8 42 | or %r0, %r9 43 | 44 | exit 45 | -- result 46 | 0x9876543210 47 | -- mem 48 | 00 01 02 03 04 05 06 07 08 09 49 | -------------------------------------------------------------------------------- /tests/ldxb-large-offset.data: -------------------------------------------------------------------------------- 1 | # Copyright (c) Big Switch Networks, Inc 2 | # SPDX-License-Identifier: Apache-2.0 3 | -- asm 4 | mov %r2, %r1 5 | mov %r8, %r1 6 | ldxb %r2, [%r2+0x80] 7 | ldxb %r8, [%r8+0x88] 8 | mov %r0, %r2 9 | add %r0, %r8 10 | exit 11 | -- mem 12 | aa bb 11 cc dd ee ff 11 13 | aa bb 11 cc dd ee ff 11 14 | aa bb 11 cc dd ee ff 11 15 | aa bb 11 cc dd ee ff 11 16 | aa bb 11 cc dd ee ff 11 17 | aa bb 11 cc dd ee ff 11 18 | aa bb 11 cc dd ee ff 11 19 | aa bb 11 cc dd ee ff 11 20 | aa bb 11 cc dd ee ff 11 21 | aa bb 11 cc dd ee ff 11 22 | aa bb 11 cc dd ee ff 11 23 | aa bb 11 cc dd ee ff 11 24 | aa bb 11 cc dd ee ff 11 25 | aa bb 11 cc dd ee ff 11 26 | aa bb 11 cc dd ee ff 11 27 | aa bb 11 cc dd ee ff 11 28 | 02 bb 11 cc dd ee ff 11 29 | 03 bb 11 cc dd ee ff 11 30 | -- result 31 | 0x05 32 | 33 | 34 | -------------------------------------------------------------------------------- /tests/ldxb-small-offset.data: -------------------------------------------------------------------------------- 1 | # Copyright (c) Big Switch Networks, Inc 2 | # SPDX-License-Identifier: Apache-2.0 3 | -- asm 4 | mov %r2, %r1 5 | mov %r8, %r1 6 | ldxb %r2, [%r2+0x08] 7 | ldxb %r8, [%r8+0x7f] 8 | mov %r0, %r2 9 | add %r0, %r8 10 | exit 11 | -- mem 12 | aa bb 11 cc dd ee ff 11 13 | 02 bb 11 cc dd ee ff 11 14 | aa bb 11 cc dd ee ff 11 15 | aa bb 11 cc dd ee ff 11 16 | aa bb 11 cc dd ee ff 11 17 | aa bb 11 cc dd ee ff 11 18 | aa bb 11 cc dd ee ff 11 19 | aa bb 11 cc dd ee ff 11 20 | aa bb 11 cc dd ee ff 11 21 | aa bb 11 cc dd ee ff 11 22 | aa bb 11 cc dd ee ff 11 23 | aa bb 11 cc dd ee ff 11 24 | aa bb 11 cc dd ee ff 11 25 | aa bb 11 cc dd ee ff 11 26 | aa bb 11 cc dd ee ff 11 27 | aa bb 11 cc dd ee ff 03 28 | aa bb 11 cc dd ee ff 11 29 | aa bb 11 cc dd ee ff 11 30 | -- result 31 | 0x05 32 | -------------------------------------------------------------------------------- /tests/ldxb.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | ldxb %r0, [%r1+2] 3 | exit 4 | -- mem 5 | aa bb 11 cc dd 6 | -- result 7 | 0x11 8 | -------------------------------------------------------------------------------- /tests/ldxdw.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | ldxdw %r0, [%r1+2] 3 | exit 4 | -- mem 5 | aa bb 11 22 33 44 55 66 77 88 cc dd 6 | -- result 7 | 0x8877665544332211 8 | -------------------------------------------------------------------------------- /tests/ldxh-all.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r0, %r1 3 | 4 | ldxh %r9, [%r0+0] 5 | be16 %r9 6 | lsh %r9, 0 7 | 8 | ldxh %r8, [%r0+2] 9 | be16 %r8 10 | lsh %r8, 4 11 | 12 | ldxh %r7, [%r0+4] 13 | be16 %r7 14 | lsh %r7, 8 15 | 16 | ldxh %r6, [%r0+6] 17 | be16 %r6 18 | lsh %r6, 12 19 | 20 | ldxh %r5, [%r0+8] 21 | be16 %r5 22 | lsh %r5, 16 23 | 24 | ldxh %r4, [%r0+10] 25 | be16 %r4 26 | lsh %r4, 20 27 | 28 | ldxh %r3, [%r0+12] 29 | be16 %r3 30 | lsh %r3, 24 31 | 32 | ldxh %r2, [%r0+14] 33 | be16 %r2 34 | lsh %r2, 28 35 | 36 | ldxh %r1, [%r0+16] 37 | be16 %r1 38 | lsh %r1, 32 39 | 40 | ldxh %r0, [%r0+18] 41 | be16 %r0 42 | lsh %r0, 36 43 | 44 | or %r0, %r1 45 | or %r0, %r2 46 | or %r0, %r3 47 | or %r0, %r4 48 | or %r0, %r5 49 | or %r0, %r6 50 | or %r0, %r7 51 | or %r0, %r8 52 | or %r0, %r9 53 | 54 | exit 55 | -- result 56 | 0x9876543210 57 | -- mem 58 | 00 00 00 01 00 02 00 03 00 04 00 05 00 06 00 07 00 08 00 09 59 | -------------------------------------------------------------------------------- /tests/ldxh-all2.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r0, %r1 3 | 4 | ldxh %r9, [%r0+0] 5 | be16 %r9 6 | 7 | ldxh %r8, [%r0+2] 8 | be16 %r8 9 | 10 | ldxh %r7, [%r0+4] 11 | be16 %r7 12 | 13 | ldxh %r6, [%r0+6] 14 | be16 %r6 15 | 16 | ldxh %r5, [%r0+8] 17 | be16 %r5 18 | 19 | ldxh %r4, [%r0+10] 20 | be16 %r4 21 | 22 | ldxh %r3, [%r0+12] 23 | be16 %r3 24 | 25 | ldxh %r2, [%r0+14] 26 | be16 %r2 27 | 28 | ldxh %r1, [%r0+16] 29 | be16 %r1 30 | 31 | ldxh %r0, [%r0+18] 32 | be16 %r0 33 | 34 | or %r0, %r1 35 | or %r0, %r2 36 | or %r0, %r3 37 | or %r0, %r4 38 | or %r0, %r5 39 | or %r0, %r6 40 | or %r0, %r7 41 | or %r0, %r8 42 | or %r0, %r9 43 | 44 | exit 45 | -- result 46 | 0x3ff 47 | -- mem 48 | 00 01 49 | 00 02 50 | 00 04 51 | 00 08 52 | 00 10 53 | 00 20 54 | 00 40 55 | 00 80 56 | 01 00 57 | 02 00 58 | -------------------------------------------------------------------------------- /tests/ldxh-same-reg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r0, %r1 3 | sth [%r0], 0x1234 4 | ldxh %r0, [%r0] 5 | exit 6 | -- mem 7 | ff ff 8 | -- result 9 | 0x1234 10 | -------------------------------------------------------------------------------- /tests/ldxh.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | ldxh %r0, [%r1+2] 3 | exit 4 | -- mem 5 | aa bb 11 22 cc dd 6 | -- result 7 | 0x2211 8 | -------------------------------------------------------------------------------- /tests/ldxw-all.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r0, %r1 3 | 4 | ldxw %r9, [%r0+0] 5 | be32 %r9 6 | 7 | ldxw %r8, [%r0+4] 8 | be32 %r8 9 | 10 | ldxw %r7, [%r0+8] 11 | be32 %r7 12 | 13 | ldxw %r6, [%r0+12] 14 | be32 %r6 15 | 16 | ldxw %r5, [%r0+16] 17 | be32 %r5 18 | 19 | ldxw %r4, [%r0+20] 20 | be32 %r4 21 | 22 | ldxw %r3, [%r0+24] 23 | be32 %r3 24 | 25 | ldxw %r2, [%r0+28] 26 | be32 %r2 27 | 28 | ldxw %r1, [%r0+32] 29 | be32 %r1 30 | 31 | ldxw %r0, [%r0+36] 32 | be32 %r0 33 | 34 | or %r0, %r1 35 | or %r0, %r2 36 | or %r0, %r3 37 | or %r0, %r4 38 | or %r0, %r5 39 | or %r0, %r6 40 | or %r0, %r7 41 | or %r0, %r8 42 | or %r0, %r9 43 | 44 | exit 45 | -- result 46 | 0x030f0f 47 | -- mem 48 | 00 00 00 01 49 | 00 00 00 02 50 | 00 00 00 04 51 | 00 00 00 08 52 | 00 00 01 00 53 | 00 00 02 00 54 | 00 00 04 00 55 | 00 00 08 00 56 | 00 01 00 00 57 | 00 02 00 00 58 | -------------------------------------------------------------------------------- /tests/ldxw.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | ldxw %r0, [%r1+2] 3 | exit 4 | -- mem 5 | aa bb 11 22 33 44 cc dd 6 | -- result 7 | 0x44332211 8 | -------------------------------------------------------------------------------- /tests/le16.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | ldxh %r0, [%r1] 3 | le16 %r0 4 | exit 5 | -- mem 6 | 22 11 7 | -- result 8 | 0x1122 9 | -------------------------------------------------------------------------------- /tests/le32.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | ldxw %r0, [%r1] 3 | le32 %r0 4 | exit 5 | -- mem 6 | 44 33 22 11 7 | -- result 8 | 0x11223344 9 | -------------------------------------------------------------------------------- /tests/le64.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | ldxdw %r0, [%r1] 3 | le64 %r0 4 | exit 5 | -- mem 6 | 88 77 66 55 44 33 22 11 7 | -- result 8 | 0x1122334455667788 9 | -------------------------------------------------------------------------------- /tests/lsh-reg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r0, 0x1 3 | mov %r7, 4 4 | lsh %r0, %r7 5 | exit 6 | -- result 7 | 0x10 8 | -------------------------------------------------------------------------------- /tests/mem-len.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r0, %r2 3 | exit 4 | -- mem 5 | 00 00 00 01 6 | 00 00 00 02 7 | -- no register offset 8 | -- result 9 | 0x8 10 | -------------------------------------------------------------------------------- /tests/mod-by-zero-imm.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 %r0, 1 3 | mod32 %r0, 0 4 | exit 5 | -- result 6 | 0x1 7 | -------------------------------------------------------------------------------- /tests/mod-by-zero-reg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 %r0, 1 3 | mov32 %r1, 0 4 | mod32 %r0, %r1 5 | exit 6 | -- result 7 | 0x1 8 | -------------------------------------------------------------------------------- /tests/mod.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 %r0, 5748 3 | mod32 %r0, 92 4 | # %r0 == 44 5 | 6 | mov32 %r1, 13 7 | mod32 %r0, %r1 8 | # %r0 == 5 9 | 10 | exit 11 | -- result 12 | 0x5 13 | -------------------------------------------------------------------------------- /tests/mod32.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | lddw %r0, 0x100000003 3 | mod32 %r0, 3 4 | exit 5 | -- result 6 | 0x0 7 | -------------------------------------------------------------------------------- /tests/mod64-by-zero-imm.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 %r0, 1 3 | mod %r0, 0 4 | exit 5 | -- result 6 | 0x1 7 | -------------------------------------------------------------------------------- /tests/mod64-by-zero-reg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 %r0, 1 3 | mov32 %r1, 0 4 | mod %r0, %r1 5 | exit 6 | -- result 7 | 0x1 8 | -------------------------------------------------------------------------------- /tests/mod64.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 %r0, 0xb1858436 3 | lsh %r0, 32 4 | or %r0, 0x100dc5c8 5 | # %r0 == 0xb1858436100dc5c8 6 | 7 | mov32 %r1, 0xdde263e 8 | lsh %r1, 32 9 | or %r1, 0x3cbef7f3 10 | # %r1 == 0xdde263e3cbef7f3 11 | 12 | mod %r0, %r1 13 | # %r0 == 0xb1bb94b371a2664 14 | 15 | mod %r0, 0x658f1778 16 | # %r0 == 0x30ba5a04 17 | 18 | exit 19 | -- result 20 | 0x30ba5a04 21 | -------------------------------------------------------------------------------- /tests/mov.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 %r1, 1 3 | mov32 %r0, %r1 4 | exit 5 | -- result 6 | 0x1 7 | -------------------------------------------------------------------------------- /tests/mov64-sign-extend.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r0, -10 3 | exit 4 | -- result 5 | 0xFFFFFFFFFFFFFFF6 6 | -------------------------------------------------------------------------------- /tests/mul-loop-memory-iterations.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r0, 0x7 3 | ldxw %r1, [%r1] 4 | add %r1, 0x2 5 | mul %r0, 0x7 6 | add %r1, 0xffffffff 7 | jne %r1, 0x0, -3 8 | exit 9 | -- mem 10 | 00 00 00 00 11 | -- result 12 | 0x157 13 | -------------------------------------------------------------------------------- /tests/mul-loop.data: -------------------------------------------------------------------------------- 1 | # Testcase compiled by Clang 2 | -- c 3 | #include 4 | 5 | uint64_t entry(void *ctx) 6 | { 7 | uint64_t n = (uintptr_t)ctx + 10; 8 | uint64_t i; 9 | uint64_t a = 7llu; 10 | for (i = 0; i < n; i++) { 11 | a *= 7llu; 12 | } 13 | return a; 14 | } 15 | -- asm 16 | mov %r0, 0x7 17 | add %r1, 0xa 18 | lsh %r1, 0x20 19 | rsh %r1, 0x20 20 | jeq %r1, 0x0, +4 21 | mov %r0, 0x7 22 | mul %r0, 0x7 23 | add %r1, 0xffffffff 24 | jne %r1, 0x0, -3 25 | exit 26 | -- result 27 | 0x75db9c97 28 | -------------------------------------------------------------------------------- /tests/mul32-imm.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r0, 3 3 | mul32 %r0, 4 4 | exit 5 | -- result 6 | 0xc 7 | -------------------------------------------------------------------------------- /tests/mul32-reg-overflow.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r0, 0x40000001 3 | mov %r1, 4 4 | mul32 %r0, %r1 5 | exit 6 | -- result 7 | 0x4 8 | -------------------------------------------------------------------------------- /tests/mul32-reg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r0, 3 3 | mov %r1, 4 4 | mul32 %r0, %r1 5 | exit 6 | -- result 7 | 0xc 8 | -------------------------------------------------------------------------------- /tests/mul64-imm.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r0, 0x40000001 3 | mul %r0, 4 4 | exit 5 | -- result 6 | 0x100000004 7 | -------------------------------------------------------------------------------- /tests/mul64-reg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r0, 0x40000001 3 | mov %r1, 4 4 | mul %r0, %r1 5 | exit 6 | -- result 7 | 0x100000004 8 | -------------------------------------------------------------------------------- /tests/neg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 %r0, 2 3 | neg32 %r0 4 | exit 5 | -- result 6 | 0xfffffffe 7 | -------------------------------------------------------------------------------- /tests/neg64.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 %r0, 2 3 | neg %r0 4 | exit 5 | -- result 6 | 0xfffffffffffffffe 7 | -------------------------------------------------------------------------------- /tests/prime.data: -------------------------------------------------------------------------------- 1 | # Compiled by Clang 2 | -- c 3 | #include 4 | #include 5 | 6 | uint64_t entry(uint64_t arg) 7 | { 8 | int i = 0; 9 | for (i = 2; i < arg; i++) { 10 | if (arg % i == 0) { 11 | return false; 12 | } 13 | } 14 | return true; 15 | } 16 | -- asm 17 | mov %r1, 67 18 | mov %r0, 0x1 19 | mov %r2, 0x2 20 | jgt %r1, 0x2, +4 21 | ja +10 22 | add %r2, 0x1 23 | mov %r0, 0x1 24 | jge %r2, %r1, +7 25 | mov %r3, %r1 26 | div %r3, %r2 27 | mul %r3, %r2 28 | mov %r4, %r1 29 | sub %r4, %r3 30 | mov %r0, 0x0 31 | jne %r4, 0x0, -10 32 | exit 33 | -- result 34 | 0x1 35 | -------------------------------------------------------------------------------- /tests/reload.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r0, 0 3 | exit 4 | -- reload 5 | -- error 6 | Failed to load code: code has already been loaded into this VM. Use ubpf_unload_code() if you need to reuse this VM 7 | -------------------------------------------------------------------------------- /tests/rsh-reg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r0, 0x10 3 | mov %r7, 4 4 | rsh %r0, %r7 5 | exit 6 | -- result 7 | 0x1 8 | -------------------------------------------------------------------------------- /tests/rsh32.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r0, 0 3 | sub %r0, 1 4 | rsh32 %r0, 8 5 | exit 6 | -- result 7 | 0x00ffffff 8 | -------------------------------------------------------------------------------- /tests/st.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | stw [%r1], 0x33 3 | stw [%r1+1], 0x33 4 | stw [%r1+32767], 0x33 5 | stw [%r1-1], 0x33 6 | stw [%r1-32768], 0x33 7 | -- raw 8 | 0x0000003300000162 9 | 0x0000003300010162 10 | 0x000000337fff0162 11 | 0x00000033ffff0162 12 | 0x0000003380000162 13 | -------------------------------------------------------------------------------- /tests/stack.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r1, 51 3 | 4 | # Create lookup table 5 | stdw [%r10-16], 0xab 6 | stdw [%r10-8], 0xcd 7 | 8 | # Load lookup[%r1 % 2] 9 | and %r1, 1 10 | lsh %r1, 3 11 | mov %r2, %r10 12 | add %r2, %r1 13 | ldxdw %r0, [%r2-16] 14 | 15 | exit 16 | -- result 17 | 0xcd 18 | -------------------------------------------------------------------------------- /tests/stack2.data: -------------------------------------------------------------------------------- 1 | # Test that stack data is preserved across function calls 2 | -- asm 3 | stb [%r10-4], 0x01 4 | stb [%r10-3], 0x02 5 | stb [%r10-2], 0x03 6 | stb [%r10-1], 0x04 7 | 8 | # memfrob 9 | mov %r1, %r10 10 | mov %r2, 0x4 11 | sub %r1, %r2 12 | call 1 13 | 14 | mov %r1, 0 15 | ldxb %r2, [%r10-4] 16 | ldxb %r3, [%r10-3] 17 | ldxb %r4, [%r10-2] 18 | ldxb %r5, [%r10-1] 19 | 20 | call 0 # gather_bytes 21 | 22 | xor %r0, 0x2a2a2a2a # undo memfrob 23 | 24 | exit 25 | -- result 26 | 0x01020304 27 | -- no register offset 28 | call instruction 29 | -------------------------------------------------------------------------------- /tests/stack3.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | stdw [%r10-256], 123 3 | stdw [%r10-512], 456 4 | mov %r2, %r10 5 | sub %r2, 256 6 | ldxdw %r0, [%r2] 7 | sub %r2, 256 8 | ldxdw %r1, [%r2] 9 | add %r0, %r1 10 | 11 | exit 12 | -- result 13 | 0x243 14 | -------------------------------------------------------------------------------- /tests/stb.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | stb [%r1+2], 0x11 3 | ldxb %r0, [%r1+2] 4 | exit 5 | -- mem 6 | aa bb ff cc dd 7 | -- result 8 | 0x11 9 | -------------------------------------------------------------------------------- /tests/stdw.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | stdw [%r1+2], 0x44332211 3 | ldxdw %r0, [%r1+2] 4 | exit 5 | -- mem 6 | aa bb ff ff ff ff ff ff ff ff cc dd 7 | -- result 8 | 0x0000000044332211 9 | -------------------------------------------------------------------------------- /tests/sth.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | sth [%r1+2], 0x2211 3 | ldxh %r0, [%r1+2] 4 | exit 5 | -- mem 6 | aa bb ff ff cc dd 7 | -- result 8 | 0x2211 9 | -------------------------------------------------------------------------------- /tests/string-stack.data: -------------------------------------------------------------------------------- 1 | -- c 2 | extern int strcmp_ext(const char *a, const char *b); 3 | 4 | int entry(int *mem) 5 | { 6 | char a[] = "abcx"; 7 | char b[] = "abcy"; 8 | 9 | if (strcmp_ext(a, a) != 0) { 10 | return 1; 11 | } 12 | 13 | if (strcmp_ext(a, b) == 0) { 14 | return 1; 15 | } 16 | 17 | return 0; 18 | } 19 | -- asm 20 | mov %r1, 0x78636261 21 | stxw [%r10-8], %r1 22 | mov %r6, 0x0 23 | stxb [%r10-4], %r6 24 | stxb [%r10-12], %r6 25 | mov %r1, 0x79636261 26 | stxw [%r10-16], %r1 27 | mov %r1, %r10 28 | add %r1, 0xfffffff8 29 | mov %r2, %r1 30 | call 0x4 31 | mov %r1, %r0 32 | mov %r0, 0x1 33 | lsh %r1, 0x20 34 | rsh %r1, 0x20 35 | jne %r1, 0x0, +11 36 | mov %r1, %r10 37 | add %r1, 0xfffffff8 38 | mov %r2, %r10 39 | add %r2, 0xfffffff0 40 | call 0x4 41 | mov %r1, %r0 42 | lsh %r1, 0x20 43 | rsh %r1, 0x20 44 | mov %r0, 0x1 45 | jeq %r1, %r6, +1 46 | mov %r0, 0x0 47 | exit 48 | -- result 49 | 0x0 50 | -- no register offset 51 | call instruction 52 | -------------------------------------------------------------------------------- /tests/stw.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | stw [%r1+2], 0x44332211 3 | ldxw %r0, [%r1+2] 4 | exit 5 | -- mem 6 | aa bb ff ff ff ff cc dd 7 | -- result 8 | 0x44332211 9 | -------------------------------------------------------------------------------- /tests/stx.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | stxw [%r1], %r2 3 | stxw [%r1+1], %r2 4 | stxw [%r1+32767], %r2 5 | stxw [%r1-1], %r2 6 | stxw [%r1-32768], %r2 7 | -- raw 8 | 0x0000000000002163 9 | 0x0000000000012163 10 | 0x000000007fff2163 11 | 0x00000000ffff2163 12 | 0x0000000080002163 13 | -------------------------------------------------------------------------------- /tests/stxb-all.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r0, 0xf0 3 | mov %r2, 0xf2 4 | mov %r3, 0xf3 5 | mov %r4, 0xf4 6 | mov %r5, 0xf5 7 | mov %r6, 0xf6 8 | mov %r7, 0xf7 9 | mov %r8, 0xf8 10 | stxb [%r1], %r0 11 | stxb [%r1+1], %r2 12 | stxb [%r1+2], %r3 13 | stxb [%r1+3], %r4 14 | stxb [%r1+4], %r5 15 | stxb [%r1+5], %r6 16 | stxb [%r1+6], %r7 17 | stxb [%r1+7], %r8 18 | ldxdw %r0, [%r1] 19 | be64 %r0 20 | exit 21 | -- mem 22 | ff ff ff ff ff ff ff ff 23 | -- result 24 | 0xf0f2f3f4f5f6f7f8 25 | -------------------------------------------------------------------------------- /tests/stxb-all2.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r0, %r1 3 | mov %r1, 0xf1 4 | mov %r9, 0xf9 5 | stxb [%r0], %r1 6 | stxb [%r0+1], %r9 7 | ldxh %r0, [%r0] 8 | be16 %r0 9 | exit 10 | -- mem 11 | ff ff 12 | -- result 13 | 0xf1f9 14 | -------------------------------------------------------------------------------- /tests/stxb-chain.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r0, %r1 3 | 4 | ldxb %r9, [%r0+0] 5 | stxb [%r0+1], %r9 6 | 7 | ldxb %r8, [%r0+1] 8 | stxb [%r0+2], %r8 9 | 10 | ldxb %r7, [%r0+2] 11 | stxb [%r0+3], %r7 12 | 13 | ldxb %r6, [%r0+3] 14 | stxb [%r0+4], %r6 15 | 16 | ldxb %r5, [%r0+4] 17 | stxb [%r0+5], %r5 18 | 19 | ldxb %r4, [%r0+5] 20 | stxb [%r0+6], %r4 21 | 22 | ldxb %r3, [%r0+6] 23 | stxb [%r0+7], %r3 24 | 25 | ldxb %r2, [%r0+7] 26 | stxb [%r0+8], %r2 27 | 28 | ldxb %r1, [%r0+8] 29 | stxb [%r0+9], %r1 30 | 31 | ldxb %r0, [%r0+9] 32 | exit 33 | -- mem 34 | 2a 00 00 00 00 00 00 00 00 00 35 | -- result 36 | 0x2a 37 | -------------------------------------------------------------------------------- /tests/stxb.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 %r2, 0x11 3 | stxb [%r1+2], %r2 4 | ldxb %r0, [%r1+2] 5 | exit 6 | -- mem 7 | aa bb ff cc dd 8 | -- result 9 | 0x11 10 | -------------------------------------------------------------------------------- /tests/stxdw.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r2, 0x88776655 3 | lsh %r2, 32 4 | or %r2, 0x44332211 5 | stxdw [%r1+2], %r2 6 | ldxdw %r0, [%r1+2] 7 | exit 8 | -- mem 9 | aa bb ff ff ff ff ff ff ff ff cc dd 10 | -- result 11 | 0x8877665544332211 12 | -------------------------------------------------------------------------------- /tests/stxh.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 %r2, 0x2211 3 | stxh [%r1+2], %r2 4 | ldxh %r0, [%r1+2] 5 | exit 6 | -- mem 7 | aa bb ff ff cc dd 8 | -- result 9 | 0x2211 10 | -------------------------------------------------------------------------------- /tests/stxw.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 %r2, 0x44332211 3 | stxw [%r1+2], %r2 4 | ldxw %r0, [%r1+2] 5 | exit 6 | -- mem 7 | aa bb ff ff ff ff cc dd 8 | -- result 9 | 0x44332211 10 | -------------------------------------------------------------------------------- /tests/subnet.data: -------------------------------------------------------------------------------- 1 | # Compiled by Clang 2 | # Check that the ipv4_dst is in 192.168.1.0/24 3 | -- c 4 | #include 5 | 6 | #define NETMASK 0xffffff00 7 | #define SUBNET 0xc0a80100 8 | 9 | struct eth_hdr { 10 | uint8_t eth_src[6]; 11 | uint8_t eth_dst[6]; 12 | uint16_t eth_type; 13 | }; 14 | 15 | struct vlan_hdr { 16 | uint16_t vlan; 17 | uint16_t eth_type; 18 | }; 19 | 20 | struct ipv4_hdr { 21 | uint8_t ver_ihl; 22 | uint8_t tos; 23 | uint16_t total_length; 24 | uint16_t id; 25 | uint16_t frag; 26 | uint8_t ttl; 27 | uint8_t proto; 28 | uint16_t csum; 29 | uint32_t src; 30 | uint32_t dst; 31 | }; 32 | 33 | uint64_t entry(void *mem) 34 | { 35 | struct eth_hdr *eth_hdr = (void *)mem; 36 | uint16_t eth_type; 37 | void *next = eth_hdr; 38 | 39 | if (eth_hdr->eth_type == __builtin_bswap16(0x8100)) { 40 | struct vlan_hdr *vlan_hdr = (void *)(eth_hdr + 1); 41 | eth_type = vlan_hdr->eth_type; 42 | next = vlan_hdr + 1; 43 | } else { 44 | eth_type = eth_hdr->eth_type; 45 | next = eth_hdr + 1; 46 | } 47 | 48 | if (eth_type == __builtin_bswap16(0x0800)) { 49 | struct ipv4_hdr *ipv4_hdr = next; 50 | if ((ipv4_hdr->dst & __builtin_bswap32(NETMASK)) == __builtin_bswap32(SUBNET)) { 51 | return 1; 52 | } 53 | } 54 | 55 | return 0; 56 | } 57 | -- asm 58 | mov %r2, 0xe 59 | ldxh %r3, [%r1+12] 60 | jne %r3, 0x81, +2 61 | mov %r2, 0x12 62 | ldxh %r3, [%r1+16] 63 | and %r3, 0xffff 64 | jne %r3, 0x8, +5 65 | add %r1, %r2 66 | mov %r0, 0x1 67 | ldxw %r1, [%r1+16] 68 | and %r1, 0xffffff 69 | jeq %r1, 0x1a8c0, +1 70 | mov %r0, 0x0 71 | exit 72 | -- mem 73 | 00 00 c0 9f a0 97 00 a0 74 | cc 3b bf fa 08 00 45 10 75 | 00 3c 46 3c 40 00 40 06 76 | 73 1c c0 a8 01 02 c0 a8 77 | 01 01 06 0e 00 17 99 c5 78 | a0 ec 00 00 00 00 a0 02 79 | 7d 78 e0 a3 00 00 02 04 80 | 05 b4 04 02 08 0a 00 9c 81 | 27 24 00 00 00 00 01 03 82 | 03 00 83 | -- result 84 | 0x1 85 | -------------------------------------------------------------------------------- /tests/tcp-port-80/match.data: -------------------------------------------------------------------------------- 1 | -- asm @ tcp-port-80.asm 2 | -- mem 3 | 00 01 02 03 04 05 00 06 07 08 09 0a 08 00 45 00 4 | 00 56 00 01 00 00 40 06 f9 4d c0 a8 00 01 c0 a8 5 | 00 02 27 10 00 50 00 00 00 00 00 00 00 00 50 02 6 | 20 00 c5 18 00 00 44 44 44 44 44 44 44 44 44 44 7 | 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 8 | 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 9 | 44 44 44 44 10 | -- result 11 | 0x1 12 | -------------------------------------------------------------------------------- /tests/tcp-port-80/nomatch-ethertype.data: -------------------------------------------------------------------------------- 1 | -- description 2 | Wrong ethertype 3 | -- asm @ tcp-port-80.asm 4 | -- mem 5 | 00 01 02 03 04 05 00 06 07 08 09 0a 08 01 45 00 6 | 00 56 00 01 00 00 40 06 f9 4d c0 a8 00 01 c0 a8 7 | 00 02 27 10 00 50 00 00 00 00 00 00 00 00 50 02 8 | 20 00 c5 18 00 00 44 44 44 44 44 44 44 44 44 44 9 | 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 10 | 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 11 | 44 44 44 44 12 | -- result 13 | 0x0 14 | -------------------------------------------------------------------------------- /tests/tcp-port-80/nomatch-proto.data: -------------------------------------------------------------------------------- 1 | -- description 2 | Wrong IP protocol 3 | -- asm @ tcp-port-80.asm 4 | -- mem 5 | 00 01 02 03 04 05 00 06 07 08 09 0a 08 00 45 00 6 | 00 56 00 01 00 00 40 11 f9 4d c0 a8 00 01 c0 a8 7 | 00 02 27 10 00 50 00 00 00 00 00 00 00 00 50 02 8 | 20 00 c5 18 00 00 44 44 44 44 44 44 44 44 44 44 9 | 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 10 | 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 11 | 44 44 44 44 12 | -- result 13 | 0x0 14 | -------------------------------------------------------------------------------- /tests/tcp-port-80/nomatch.data: -------------------------------------------------------------------------------- 1 | -- description 2 | Incorrect TCP src/dst ports 3 | -- asm @ tcp-port-80.asm 4 | -- mem 5 | 00 01 02 03 04 05 00 06 07 08 09 0a 08 00 45 00 6 | 00 56 00 01 00 00 40 06 f9 4d c0 a8 00 01 c0 a8 7 | 00 02 00 16 27 10 00 00 00 00 00 00 00 00 51 02 8 | 20 00 c5 18 00 00 44 44 44 44 44 44 44 44 44 44 9 | 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 10 | 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 11 | 44 44 44 44 12 | -- result 13 | 0x0 14 | -------------------------------------------------------------------------------- /tests/tcp-port-80/tcp-port-80.asm: -------------------------------------------------------------------------------- 1 | ldxb %r2, [%r1+12] 2 | ldxb %r3, [%r1+13] 3 | lsh %r3, 0x8 4 | or %r3, %r2 5 | mov %r0, 0x0 6 | jne %r3, 0x8, +12 7 | ldxb %r2, [%r1+23] 8 | jne %r2, 0x6, +10 9 | ldxb %r2, [%r1+14] 10 | add %r1, 0xe 11 | and %r2, 0xf 12 | lsh %r2, 0x2 13 | add %r1, %r2 14 | ldxh %r2, [%r1+2] 15 | jeq %r2, 0x5000, +2 16 | ldxh %r1, [%r1] 17 | jne %r1, 0x5000, +1 18 | mov %r0, 0x1 19 | exit 20 | -------------------------------------------------------------------------------- /tests/tcp-sack/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.bin 3 | -------------------------------------------------------------------------------- /tests/tcp-sack/match.data: -------------------------------------------------------------------------------- 1 | -- asm @ tcp-sack.asm 2 | -- mem @ pkt-sack.hex 3 | -- result 4 | 0x1 5 | -------------------------------------------------------------------------------- /tests/tcp-sack/nomatch.data: -------------------------------------------------------------------------------- 1 | -- asm @ tcp-sack.asm 2 | -- mem @ pkt-nosack.hex 3 | -- result 4 | 0x0 5 | -------------------------------------------------------------------------------- /tests/tcp-sack/pkt-nosack.hex: -------------------------------------------------------------------------------- 1 | 0x0000: 0026 622f 4787 001d 60b3 0184 0800 4500 2 | 0x0010: 0040 a8de 4000 4006 9d58 c0a8 0103 3f74 3 | 0x0020: f361 e5c0 0050 e594 3f77 a3c4 c480 8010 4 | 0x0030: 013e 34b6 0000 0101 080a 0017 956f 8d9d 5 | 0x0040: 9e27 6 | -------------------------------------------------------------------------------- /tests/tcp-sack/pkt-sack.hex: -------------------------------------------------------------------------------- 1 | 0x0000: 0026 622f 4787 001d 60b3 0184 0800 4500 2 | 0x0010: 0040 a8de 4000 4006 9d58 c0a8 0103 3f74 3 | 0x0020: f361 e5c0 0050 e594 3f77 a3c4 c480 b010 4 | 0x0030: 013e 34b6 0000 0101 080a 0017 956f 8d9d 5 | 0x0040: 9e27 0101 050a a3c4 ca28 a3c4 cfd0 6 | -------------------------------------------------------------------------------- /tests/tcp-sack/tcp-sack.asm: -------------------------------------------------------------------------------- 1 | ldxb %r2, [%r1+12] 2 | ldxb %r3, [%r1+13] 3 | lsh %r3, 0x8 4 | or %r3, %r2 5 | mov %r0, 0x0 6 | jne %r3, 0x8, +37 7 | ldxb %r2, [%r1+23] 8 | jne %r2, 0x6, +35 9 | ldxb %r2, [%r1+14] 10 | add %r1, 0xe 11 | and %r2, 0xf 12 | lsh %r2, 0x2 13 | add %r1, %r2 14 | mov %r0, 0x0 15 | ldxh %r4, [%r1+12] 16 | add %r1, 0x14 17 | rsh %r4, 0x2 18 | and %r4, 0x3c 19 | mov %r2, %r4 20 | add %r2, 0xffffffec 21 | mov %r5, 0x15 22 | mov %r3, 0x0 23 | jgt %r5, %r4, +20 24 | mov %r5, %r3 25 | lsh %r5, 0x20 26 | arsh %r5, 0x20 27 | mov %r4, %r1 28 | add %r4, %r5 29 | ldxb %r5, [%r4] 30 | jeq %r5, 0x1, +4 31 | jeq %r5, 0x0, +12 32 | mov %r6, %r3 33 | jeq %r5, 0x5, +9 34 | ja +2 35 | add %r3, 0x1 36 | mov %r6, %r3 37 | ldxb %r3, [%r4+1] 38 | add %r3, %r6 39 | lsh %r3, 0x20 40 | arsh %r3, 0x20 41 | jsgt %r2, %r3, -18 42 | ja +1 43 | mov %r0, 0x1 44 | exit 45 | -------------------------------------------------------------------------------- /tests/tcp-sack/tcp-sack.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | struct tcp_option 7 | { 8 | uint8_t kind; 9 | uint8_t length; 10 | }; 11 | 12 | uint64_t 13 | entry(void* pkt) 14 | { 15 | struct ether_header* ether_header = (void*)pkt; 16 | 17 | if (ether_header->ether_type != __builtin_bswap16(0x0800)) { 18 | return 0; 19 | } 20 | 21 | struct iphdr* iphdr = (void*)(ether_header + 1); 22 | if (iphdr->protocol != 6) { 23 | return 0; 24 | } 25 | 26 | struct tcphdr* tcphdr = (void*)iphdr + iphdr->ihl * 4; 27 | 28 | void* options_start = (void*)(tcphdr + 1); 29 | int options_length = tcphdr->doff * 4 - sizeof(*tcphdr); 30 | int offset = 0; 31 | 32 | while (offset < options_length) { 33 | struct tcp_option* option = options_start + offset; 34 | if (option->kind == 0) { 35 | /* End of options */ 36 | break; 37 | } else if (option->kind == 1) { 38 | /* NOP */ 39 | offset++; 40 | } else if (option->kind == 5) { 41 | /* SACK */ 42 | return 1; 43 | } 44 | 45 | offset += option->length; 46 | } 47 | 48 | return 0; 49 | } 50 | -------------------------------------------------------------------------------- /tests/unload_reload.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov %r0, 0 3 | exit 4 | -- unload 5 | -- result 6 | 0x0 7 | -------------------------------------------------------------------------------- /ubpf/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iovisor/ubpf/a568817976ba29a31a13c0d5642c91d38c33a4b0/ubpf/__init__.py -------------------------------------------------------------------------------- /ubpf/asm_parser.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from __future__ import print_function 3 | from parcon import * 4 | from collections import namedtuple 5 | 6 | hexchars = '0123456789abcdefABCDEF' 7 | 8 | Reg = namedtuple("Reg", ["num"]) 9 | Imm = namedtuple("Imm", ["value"]) 10 | MemRef = namedtuple("MemRef", ["reg", "offset"]) 11 | 12 | def keywords(vs): 13 | return First(*[Keyword(SignificantLiteral(v)) for v in vs]) 14 | 15 | hexnum = SignificantLiteral('0x') + +CharIn(hexchars) 16 | decnum = +Digit() 17 | offset = (CharIn("+-") + Exact(hexnum | decnum))[flatten]["".join][lambda x: int(x, 0)] 18 | imm = (-CharIn("+-") + Exact(hexnum | decnum))[flatten]["".join][lambda x: int(x, 0)][Imm] 19 | 20 | reg = Literal('%') + Literal('r') + integer[int][Reg] 21 | memref = (Literal('[') + reg + Optional(offset, 0) + Literal(']'))[lambda x: MemRef(*x)] 22 | 23 | unary_alu_ops = ['neg', 'neg32', 'le16', 'le32', 'le64', 'be16', 'be32', 'be64'] 24 | binary_alu_ops = ['add', 'sub', 'mul', 'div', 'or', 'and', 'lsh', 'rsh', 25 | 'mod', 'xor', 'mov', 'arsh'] 26 | binary_alu_ops.extend([x + '32' for x in binary_alu_ops]) 27 | 28 | alu_instruction = \ 29 | (keywords(unary_alu_ops) + reg) | \ 30 | (keywords(binary_alu_ops) + reg + "," + (reg | imm)) 31 | 32 | mem_sizes = ['w', 'h', 'b', 'dw'] 33 | mem_store_reg_ops = ['stx' + s for s in mem_sizes] 34 | mem_store_imm_ops = ['st' + s for s in mem_sizes] 35 | mem_load_ops = ['ldx' + s for s in mem_sizes] 36 | 37 | mem_instruction = \ 38 | (keywords(mem_store_reg_ops) + memref + "," + reg) | \ 39 | (keywords(mem_store_imm_ops) + memref + "," + imm) | \ 40 | (keywords(mem_load_ops) + reg + "," + memref) | \ 41 | (keywords(["lddw"]) + reg + "," + imm) 42 | 43 | jmp_cmp_ops = ['jeq', 'jgt', 'jge', 'jlt', 'jle', 'jset', 'jne', 'jsgt', 'jsge', 'jslt', 'jsle'] 44 | jmp_instruction = \ 45 | (keywords(jmp_cmp_ops) + reg + "," + (reg | imm) + "," + offset) | \ 46 | (keywords(['ja']) + offset) | \ 47 | (keywords(['call']) + imm) | \ 48 | (keywords(['exit'])[lambda x: (x, )]) 49 | 50 | instruction = alu_instruction | mem_instruction | jmp_instruction 51 | 52 | start = ZeroOrMore(instruction + Optional(Literal(';'))) + End() 53 | 54 | def parse(source): 55 | return start.parse_string(source) 56 | 57 | if __name__ == "__main__": 58 | import argparse 59 | parser = argparse.ArgumentParser(description="Assembly parser", formatter_class=argparse.RawDescriptionHelpFormatter) 60 | parser.add_argument('file', type=argparse.FileType('r'), default='-') 61 | args = parser.parse_args() 62 | result = parse(args.file.read()) 63 | for inst in result: 64 | print(repr(inst)) 65 | -------------------------------------------------------------------------------- /ubpf_plugin/test_helpers.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #pragma once 5 | #include "ubpf.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #if !defined(UNREFERENCED_PARAMETER) 12 | #define UNREFERENCED_PARAMETER(P) (void)(P) 13 | #endif 14 | 15 | static uint64_t 16 | gather_bytes(uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint64_t e) 17 | { 18 | return ((uint64_t)(a & 0xff) << 32) | ((uint64_t)(b & 0xff) << 24) | ((uint64_t)(c & 0xff) << 16) | 19 | ((uint64_t)(d & 0xff) << 8) | (e & 0xff); 20 | }; 21 | 22 | static uint64_t 23 | memfrob(uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint64_t e) 24 | { 25 | UNREFERENCED_PARAMETER(c); 26 | UNREFERENCED_PARAMETER(d); 27 | UNREFERENCED_PARAMETER(e); 28 | 29 | uint8_t* p = reinterpret_cast(a); 30 | for (uint64_t i = 0; i < b; i++) { 31 | p[i] ^= 42; 32 | } 33 | return 0; 34 | }; 35 | 36 | static uint64_t 37 | no_op(uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint64_t e) 38 | { 39 | UNREFERENCED_PARAMETER(a); 40 | UNREFERENCED_PARAMETER(b); 41 | UNREFERENCED_PARAMETER(c); 42 | UNREFERENCED_PARAMETER(d); 43 | UNREFERENCED_PARAMETER(e); 44 | 45 | return 0; 46 | } 47 | 48 | static uint64_t 49 | sqrti(uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint64_t e) 50 | { 51 | UNREFERENCED_PARAMETER(b); 52 | UNREFERENCED_PARAMETER(c); 53 | UNREFERENCED_PARAMETER(d); 54 | UNREFERENCED_PARAMETER(e); 55 | 56 | return static_cast(std::sqrt(a)); 57 | } 58 | 59 | static uint64_t 60 | strcmp_ext(uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint64_t e) 61 | { 62 | UNREFERENCED_PARAMETER(c); 63 | UNREFERENCED_PARAMETER(d); 64 | UNREFERENCED_PARAMETER(e); 65 | return strcmp(reinterpret_cast(a), reinterpret_cast(b)); 66 | } 67 | 68 | static uint64_t 69 | unwind(uint64_t a, uint64_t b, uint64_t c, uint64_t d, uint64_t e) 70 | { 71 | UNREFERENCED_PARAMETER(b); 72 | UNREFERENCED_PARAMETER(c); 73 | UNREFERENCED_PARAMETER(d); 74 | UNREFERENCED_PARAMETER(e); 75 | return a; 76 | } 77 | 78 | static std::map helper_functions = { 79 | {0, gather_bytes}, 80 | {1, memfrob}, 81 | {2, no_op}, 82 | {3, sqrti}, 83 | {4, strcmp_ext}, 84 | {5, unwind}, 85 | }; -------------------------------------------------------------------------------- /vm/.gitignore: -------------------------------------------------------------------------------- 1 | libubpf.a 2 | test 3 | *.o 4 | *.gcov 5 | *.gcda 6 | *.gcno 7 | -------------------------------------------------------------------------------- /vm/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022-present, IO Visor Project 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | # 5 | # Copyright (c) 2022-present, IO Visor Project 6 | # All rights reserved. 7 | # 8 | # This source code is licensed in accordance with the terms specified in 9 | # the LICENSE file found in the root directory of this source tree. 10 | # 11 | 12 | include("CheckLibraryExists") 13 | include("GNUInstallDirs") 14 | 15 | find_path(UBPF_ELF_H_PATH "elf.h" NO_CACHE) 16 | if(UBPF_ELF_H_PATH) 17 | set(UBPF_HAS_ELF_H true) 18 | elseif(PLATFORM_MACOS) 19 | message(WARNING "ubpf - using compat ELF support for macOS.") 20 | set(UBPF_ELF_H_PATH_COMPAT "${PROJECT_SOURCE_DIR}/compat/macOS/") 21 | set(UBPF_HAS_ELF_H_COMPAT true) 22 | set(UBPF_HAS_ELF_H true) 23 | else() 24 | message(WARNING "ubpf - elf.h was not found, disabling ELF support") 25 | endif() 26 | 27 | configure_file( 28 | ubpf_config.h.inc 29 | "${CMAKE_CURRENT_BINARY_DIR}/ubpf_config.h" 30 | ) 31 | 32 | set(public_header_list 33 | inc/ubpf.h 34 | "${CMAKE_CURRENT_BINARY_DIR}/ubpf_config.h" 35 | ) 36 | 37 | add_library("ubpf" 38 | ${public_header_list} 39 | 40 | ebpf.h 41 | ubpf_instruction_valid.c 42 | ubpf_int.h 43 | ubpf_jit_arm64.c 44 | ubpf_jit.c 45 | ubpf_jit_support.c 46 | ubpf_jit_support.h 47 | ubpf_jit_x86_64.c 48 | ubpf_loader.c 49 | ubpf_vm.c 50 | ) 51 | 52 | target_link_libraries("ubpf" 53 | PRIVATE 54 | "ubpf_settings" 55 | ) 56 | 57 | target_include_directories("ubpf" PUBLIC 58 | $ 59 | $ 60 | $ 61 | $ 62 | ) 63 | 64 | set_target_properties("ubpf" PROPERTIES 65 | PUBLIC_HEADER 66 | "${public_header_list}" 67 | ) 68 | 69 | if(PLATFORM_LINUX) 70 | check_library_exists("m" "pow" "" "libm_found") 71 | 72 | if(libm_found) 73 | target_link_libraries("ubpf" 74 | PUBLIC 75 | "m" 76 | ) 77 | endif() 78 | endif() 79 | 80 | if(UBPF_ENABLE_TESTS) 81 | add_executable("ubpf_test" 82 | test.c 83 | ) 84 | 85 | target_link_libraries("ubpf_test" 86 | PRIVATE 87 | "ubpf_settings" 88 | "ubpf" 89 | ) 90 | endif() 91 | 92 | add_subdirectory("compat") 93 | 94 | if(TARGET "ubpf_compat") 95 | target_link_libraries("ubpf" PRIVATE 96 | $ 97 | ) 98 | 99 | if(UBPF_ENABLE_TESTS) 100 | target_link_libraries("ubpf_test" PRIVATE 101 | $ 102 | ) 103 | endif() 104 | 105 | endif() 106 | 107 | 108 | if(UBPF_ENABLE_INSTALL) 109 | install( 110 | TARGETS 111 | "ubpf" 112 | 113 | EXPORT 114 | "ubpf" 115 | 116 | LIBRARY DESTINATION 117 | "${CMAKE_INSTALL_LIBDIR}" 118 | 119 | PUBLIC_HEADER DESTINATION 120 | "${CMAKE_INSTALL_INCLUDEDIR}" 121 | ) 122 | 123 | install( 124 | EXPORT 125 | "ubpf" 126 | 127 | DESTINATION 128 | "${CMAKE_INSTALL_LIBDIR}/cmake/ubpf" 129 | 130 | NAMESPACE 131 | "ubpf::" 132 | 133 | FILE 134 | "ubpfConfig.cmake" 135 | ) 136 | endif() 137 | -------------------------------------------------------------------------------- /vm/compat/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2022-present, IO Visor Project 3 | # All rights reserved. 4 | # 5 | # This source code is licensed in accordance with the terms specified in 6 | # the LICENSE file found in the root directory of this source tree. 7 | # 8 | 9 | if(PLATFORM_WINDOWS) 10 | add_subdirectory("libraries") 11 | 12 | add_library("ubpf_compat" INTERFACE) 13 | target_sources("ubpf_compat" INTERFACE 14 | windows/sys/mman.h 15 | windows/sys/mman.c 16 | 17 | windows/endian.h 18 | 19 | windows/unistd.h 20 | windows/unistd.c 21 | ) 22 | 23 | target_include_directories("ubpf_compat" INTERFACE 24 | windows 25 | ) 26 | 27 | target_link_libraries("ubpf_compat" INTERFACE 28 | "external::win-c" 29 | "Ws2_32.lib" 30 | ) 31 | 32 | elseif(PLATFORM_MACOS) 33 | add_library("ubpf_compat" INTERFACE) 34 | 35 | target_sources("ubpf_compat" INTERFACE 36 | macos/endian.h 37 | ) 38 | 39 | target_include_directories("ubpf_compat" INTERFACE 40 | macos 41 | ) 42 | endif() 43 | -------------------------------------------------------------------------------- /vm/compat/libraries/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2022-present, IO Visor Project 3 | # All rights reserved. 4 | # 5 | # This source code is licensed in accordance with the terms specified in 6 | # the LICENSE file found in the root directory of this source tree. 7 | # 8 | 9 | add_subdirectory("win-c") 10 | -------------------------------------------------------------------------------- /vm/compat/libraries/win-c/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2022-present, IO Visor Project 3 | # All rights reserved. 4 | # 5 | # This source code is licensed in accordance with the terms specified in 6 | # the LICENSE file found in the root directory of this source tree. 7 | # 8 | 9 | set(library_root "${CMAKE_CURRENT_SOURCE_DIR}/src") 10 | 11 | add_library("win-c" EXCLUDE_FROM_ALL 12 | ${library_root}/include/getopt.h 13 | ${library_root}/source/getopt.c 14 | ) 15 | 16 | target_include_directories("win-c" PUBLIC 17 | ${library_root}/include 18 | ) 19 | 20 | target_link_libraries("win-c" PRIVATE 21 | "ubpf_settings" 22 | ) 23 | 24 | add_library("external::win-c" ALIAS "win-c") 25 | -------------------------------------------------------------------------------- /vm/compat/macos/endian.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 IO Visor Project 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | /* 5 | Copyright (c) 2022-present, IO Visor Project 6 | All rights reserved. 7 | 8 | This source code is licensed in accordance with the terms specified in 9 | the LICENSE file found in the root directory of this source tree. 10 | */ 11 | 12 | #pragma once 13 | 14 | #include 15 | 16 | #define htole16(value) OSSwapHostToLittleInt16(value) 17 | 18 | #define htole32(value) OSSwapHostToLittleInt32(value) 19 | 20 | #define htole64(value) OSSwapHostToLittleInt64(value) 21 | 22 | #define htobe16(value) OSSwapHostToBigInt16(value) 23 | 24 | #define htobe32(value) OSSwapHostToBigInt32(value) 25 | 26 | #define htobe64(value) OSSwapHostToBigInt64(value) 27 | -------------------------------------------------------------------------------- /vm/compat/windows/endian.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 IO Visor Project 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | /* 5 | Copyright (c) 2022-present, IO Visor Project 6 | All rights reserved. 7 | 8 | This source code is licensed in accordance with the terms specified in 9 | the LICENSE file found in the root directory of this source tree. 10 | */ 11 | 12 | #pragma once 13 | 14 | #include 15 | 16 | #define htole16(value) (uint16_t)(value) 17 | 18 | #define htole32(value) (uint32_t)(value) 19 | 20 | #define htole64(value) (value) 21 | 22 | #define htobe16(value) htons(value) 23 | 24 | #define htobe32(value) htonl(value) 25 | 26 | #define htobe64(value) htonll(value) 27 | -------------------------------------------------------------------------------- /vm/compat/windows/sys/mman.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2022-present, IO Visor Project 3 | All rights reserved. 4 | 5 | This source code is licensed in accordance with the terms specified in 6 | the LICENSE file found in the root directory of this source tree. 7 | */ 8 | 9 | #include "mman.h" 10 | 11 | #include 12 | #include 13 | 14 | #pragma comment(lib, "mincore") 15 | 16 | #define PAGE_SIZE 4096 17 | #define ALIGN_PAGE(x) (((x) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)) 18 | 19 | DWORD 20 | translate_mprotect_to_windows(int prot) 21 | { 22 | DWORD result = 0; 23 | 24 | switch (prot) { 25 | case PROT_READ: 26 | result = PAGE_READONLY; 27 | break; 28 | case PROT_WRITE: 29 | case PROT_READ | PROT_WRITE: 30 | result = PAGE_READWRITE; 31 | break; 32 | case PROT_EXEC | PROT_READ: 33 | result = PAGE_EXECUTE_READ; 34 | break; 35 | default: 36 | fprintf(stderr, "Unsupported mprotect flag: %d\n", prot); 37 | break; 38 | } 39 | return result; 40 | } 41 | 42 | void* 43 | mmap(void* addr, size_t length, int prot, int flags, int fd, off_t offset) 44 | { 45 | (void)addr; 46 | (void)length; 47 | (void)prot; 48 | (void)flags; 49 | (void)fd; 50 | (void)offset; 51 | 52 | if (fd != -1) { 53 | fprintf(stderr, "mmap: fd not supported\n"); 54 | return MAP_FAILED; 55 | } 56 | 57 | if (flags != (MAP_PRIVATE | MAP_ANONYMOUS)) { 58 | fprintf(stderr, "mmap: flags not supported\n"); 59 | return MAP_FAILED; 60 | } 61 | 62 | if (offset != 0) { 63 | fprintf(stderr, "mmap: offset not supported\n"); 64 | return MAP_FAILED; 65 | } 66 | 67 | length = ALIGN_PAGE(length); 68 | prot = translate_mprotect_to_windows(prot); 69 | 70 | void* memory = VirtualAlloc2(GetCurrentProcess(), addr, length, MEM_COMMIT | MEM_RESERVE, prot, NULL, 0); 71 | if (memory == NULL) { 72 | fprintf(stderr, "VirtualAlloc2 failed with error %d\n", GetLastError()); 73 | return MAP_FAILED; 74 | } 75 | return memory; 76 | } 77 | 78 | int 79 | munmap(void* addr, size_t length) 80 | { 81 | (void)addr; 82 | if (!VirtualFree(addr, 0, MEM_RELEASE)) { 83 | fprintf(stderr, "VirtualFree failed with error %d\n", GetLastError()); 84 | return -1; 85 | } else { 86 | return 0; 87 | } 88 | } 89 | 90 | int 91 | mprotect(void* addr, size_t len, int prot) 92 | { 93 | DWORD old_protect; 94 | if (!VirtualProtect(addr, len, translate_mprotect_to_windows(prot), &old_protect)) { 95 | fprintf(stderr, "VirtualProtect failed with error %d\n", GetLastError()); 96 | return -1; 97 | } else { 98 | return 0; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /vm/compat/windows/sys/mman.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 IO Visor Project 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | /* 5 | Copyright (c) 2022-present, IO Visor Project 6 | All rights reserved. 7 | 8 | This source code is licensed in accordance with the terms specified in 9 | the LICENSE file found in the root directory of this source tree. 10 | */ 11 | 12 | #pragma once 13 | 14 | #include 15 | #include 16 | 17 | #define PROT_READ 0x1 18 | #define PROT_WRITE 0x2 19 | #define MAP_PRIVATE 0 20 | #define MAP_ANONYMOUS 0 21 | #define MAP_FAILED NULL 22 | #define PROT_EXEC 0x4 23 | 24 | #ifdef __cplusplus 25 | extern "C" 26 | { 27 | #endif 28 | 29 | void* 30 | mmap(void* addr, size_t length, int prot, int flags, int fd, off_t offset); 31 | int 32 | munmap(void* addr, size_t length); 33 | int 34 | mprotect(void* addr, size_t len, int prot); 35 | 36 | #ifdef __cplusplus 37 | } 38 | #endif 39 | -------------------------------------------------------------------------------- /vm/compat/windows/unistd.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 IO Visor Project 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | /* 5 | Copyright (c) 2022-present, IO Visor Project 6 | All rights reserved. 7 | 8 | This source code is licensed in accordance with the terms specified in 9 | the LICENSE file found in the root directory of this source tree. 10 | */ 11 | 12 | #include "unistd.h" 13 | 14 | #define _CRT_RAND_S 15 | #include 16 | 17 | #include 18 | 19 | int 20 | rand_r(unsigned int* seedp) 21 | { 22 | (void)seedp; 23 | 24 | unsigned int value = 0; 25 | rand_s(&value); 26 | 27 | return (int)value; 28 | } 29 | 30 | int 31 | vasprintf(char** strp, const char* fmt, va_list ap) 32 | { 33 | int buffer_size = vsnprintf(NULL, 0, fmt, ap); 34 | if (buffer_size < 0) { 35 | return -1; 36 | } 37 | 38 | buffer_size++; 39 | 40 | *strp = (char*)malloc(buffer_size); 41 | if (*strp == NULL) { 42 | return -1; 43 | } 44 | 45 | return vsprintf_s(*strp, buffer_size, fmt, ap); 46 | } 47 | -------------------------------------------------------------------------------- /vm/compat/windows/unistd.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022 IO Visor Project 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | /* 5 | Copyright (c) 2022-present, IO Visor Project 6 | All rights reserved. 7 | 8 | This source code is licensed in accordance with the terms specified in 9 | the LICENSE file found in the root directory of this source tree. 10 | */ 11 | 12 | #pragma once 13 | 14 | #include 15 | 16 | #define STDIN_FILENO 0 17 | 18 | #ifdef __cplusplus 19 | extern "C" 20 | { 21 | #endif 22 | 23 | int 24 | rand_r(unsigned int* seedp); 25 | int 26 | vasprintf(char** strp, const char* fmt, va_list ap); 27 | 28 | 29 | #ifdef __cplusplus 30 | } 31 | #endif 32 | -------------------------------------------------------------------------------- /vm/ubpf_config.h.inc: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2022-present, IO Visor Project 3 | All rights reserved. 4 | 5 | This source code is licensed in accordance with the terms specified in 6 | the LICENSE file found in the root directory of this source tree. 7 | */ 8 | 9 | #pragma once 10 | 11 | #cmakedefine UBPF_DISABLE_RETPOLINES 12 | #cmakedefine UBPF_HAS_ELF_H 13 | #cmakedefine UBPF_HAS_ELF_H_COMPAT 14 | -------------------------------------------------------------------------------- /vm/ubpf_jit_support.c: -------------------------------------------------------------------------------- 1 | // Copyright (c) Will Hawkins 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | /* 5 | * Copyright Will Hawkins 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | */ 19 | 20 | #include "ubpf_jit_support.h" 21 | #include 22 | #include "ubpf.h" 23 | #include "ubpf_int.h" 24 | #include 25 | 26 | int 27 | initialize_jit_state_result( 28 | struct jit_state* state, 29 | struct ubpf_jit_result* compile_result, 30 | uint8_t* buffer, 31 | uint32_t size, 32 | enum JitMode jit_mode, 33 | char** errmsg) 34 | { 35 | compile_result->compile_result = UBPF_JIT_COMPILE_FAILURE; 36 | compile_result->errmsg = NULL; 37 | compile_result->external_dispatcher_offset = 0; 38 | compile_result->jit_mode = jit_mode; 39 | 40 | state->offset = 0; 41 | state->size = size; 42 | state->buf = buffer; 43 | state->pc_locs = calloc(UBPF_MAX_INSTS + 1, sizeof(state->pc_locs[0])); 44 | state->jumps = calloc(UBPF_MAX_INSTS, sizeof(state->jumps[0])); 45 | state->loads = calloc(UBPF_MAX_INSTS, sizeof(state->loads[0])); 46 | state->leas = calloc(UBPF_MAX_INSTS, sizeof(state->leas[0])); 47 | state->local_calls = calloc(UBPF_MAX_INSTS, sizeof(state->local_calls[0])); 48 | state->num_jumps = 0; 49 | state->num_loads = 0; 50 | state->num_leas = 0; 51 | state->num_local_calls = 0; 52 | state->jit_status = NoError; 53 | state->jit_mode = jit_mode; 54 | state->bpf_function_prolog_size = 0; 55 | 56 | if (!state->pc_locs || !state->jumps || !state->loads || !state->leas) { 57 | *errmsg = ubpf_error("Could not allocate space needed to JIT compile eBPF program"); 58 | return -1; 59 | } 60 | 61 | return 0; 62 | } 63 | 64 | void 65 | release_jit_state_result(struct jit_state* state, struct ubpf_jit_result* compile_result) 66 | { 67 | UNUSED_PARAMETER(compile_result); 68 | free(state->pc_locs); 69 | state->pc_locs = NULL; 70 | free(state->jumps); 71 | state->jumps = NULL; 72 | free(state->loads); 73 | state->loads = NULL; 74 | free(state->leas); 75 | state->leas = NULL; 76 | free(state->local_calls); 77 | state->local_calls = NULL; 78 | } 79 | 80 | void 81 | emit_patchable_relative(struct patchable_relative* table, 82 | uint32_t offset, struct PatchableTarget target, size_t index) 83 | { 84 | struct patchable_relative* jump = &table[index]; 85 | jump->offset_loc = offset; 86 | jump->target = target; 87 | } 88 | 89 | void 90 | note_load(struct jit_state* state, struct PatchableTarget target) 91 | { 92 | emit_patchable_relative(state->loads, state->offset, target, state->num_loads++); 93 | } 94 | 95 | void 96 | note_lea(struct jit_state* state, struct PatchableTarget target) 97 | { 98 | emit_patchable_relative(state->leas, state->offset, target, state->num_leas++); 99 | } 100 | 101 | void 102 | modify_patchable_relatives_target(struct patchable_relative* table, size_t table_size, uint32_t patchable_relative_src, struct PatchableTarget target) 103 | { 104 | for (size_t index = 0; index < table_size; index++) { 105 | if (table[index].offset_loc == patchable_relative_src) { 106 | table[index].target = target; 107 | } 108 | } 109 | } 110 | 111 | void 112 | emit_jump_target(struct jit_state* state, uint32_t jump_src) 113 | { 114 | DECLARE_PATCHABLE_TARGET(pt); 115 | pt.is_special = false; 116 | pt.target.regular.jit_target_pc = state->offset; 117 | 118 | modify_patchable_relatives_target(state->jumps, state->num_jumps, jump_src, pt); 119 | } 120 | --------------------------------------------------------------------------------