From 723ac057190b289c4f228802f58f6748f85b9a0f Mon Sep 17 00:00:00 2001 From: linfeng Date: Fri, 20 Jun 2025 10:49:59 +0800 Subject: [PATCH] feat: Update rbpf to mainline (#1209) * feat: Update rbpf to mainline Enable JIT compilation on x86 platform. Signed-off-by: Godones * fix: remove rbpf test Signed-off-by: Godones * fix: remove rbpf info Signed-off-by: Godones --------- Signed-off-by: Godones --- .github/workflows/makefile.yml | 2 +- kernel/Cargo.lock | 581 +--- kernel/Cargo.toml | 2 +- kernel/Makefile | 5 +- kernel/crates/rbpf/.appveyor.yml | 21 - kernel/crates/rbpf/.gitignore | 2 - kernel/crates/rbpf/Cargo.toml | 78 - kernel/crates/rbpf/LICENSE-APACHE | 202 -- kernel/crates/rbpf/LICENSE-MIT | 25 - kernel/crates/rbpf/README.md | 743 ----- kernel/crates/rbpf/clippy.toml | 1 - kernel/crates/rbpf/examples/disassemble.rs | 26 - kernel/crates/rbpf/examples/helper.rs | 3 - kernel/crates/rbpf/examples/load_elf.rs | 115 - .../rbpf/examples/load_elf__block_a_port.c | 43 - kernel/crates/rbpf/examples/rbpf_plugin.rs | 126 - kernel/crates/rbpf/examples/to_json.rs | 74 - kernel/crates/rbpf/examples/uptime.rs | 78 - kernel/crates/rbpf/mk/appveyor.bat | 72 - kernel/crates/rbpf/rustfmt.toml | 3 - kernel/crates/rbpf/src/asm_parser.rs | 642 ---- kernel/crates/rbpf/src/assembler.rs | 277 -- kernel/crates/rbpf/src/cranelift.rs | 1230 ------- kernel/crates/rbpf/src/disassembler.rs | 807 ----- kernel/crates/rbpf/src/ebpf.rs | 635 ---- kernel/crates/rbpf/src/helpers.rs | 488 --- kernel/crates/rbpf/src/insn_builder.rs | 2199 ------------- kernel/crates/rbpf/src/interpreter.rs | 708 ---- kernel/crates/rbpf/src/jit.rs | 1054 ------ kernel/crates/rbpf/src/lib.rs | 1782 ---------- kernel/crates/rbpf/src/no_std_error.rs | 41 - kernel/crates/rbpf/src/stack.rs | 75 - kernel/crates/rbpf/src/verifier.rs | 386 --- kernel/crates/rbpf/tests/assembler.rs | 655 ---- kernel/crates/rbpf/tests/common.rs | 97 - kernel/crates/rbpf/tests/cranelift.rs | 2257 ------------- kernel/crates/rbpf/tests/disassembler.rs | 377 --- kernel/crates/rbpf/tests/misc.rs | 571 ---- kernel/crates/rbpf/tests/ubpf_jit_x86_64.rs | 2891 ----------------- kernel/crates/rbpf/tests/ubpf_verifier.rs | 177 - kernel/crates/rbpf/tests/ubpf_vm.rs | 2674 --------------- kernel/src/perf/kprobe.rs | 59 +- kernel/src/perf/mod.rs | 107 +- kernel/src/perf/tracepoint.rs | 54 +- triagebot.toml | 2 - 45 files changed, 190 insertions(+), 22257 deletions(-) delete mode 100644 kernel/crates/rbpf/.appveyor.yml delete mode 100644 kernel/crates/rbpf/.gitignore delete mode 100644 kernel/crates/rbpf/Cargo.toml delete mode 100644 kernel/crates/rbpf/LICENSE-APACHE delete mode 100644 kernel/crates/rbpf/LICENSE-MIT delete mode 100644 kernel/crates/rbpf/README.md delete mode 100644 kernel/crates/rbpf/clippy.toml delete mode 100644 kernel/crates/rbpf/examples/disassemble.rs delete mode 100644 kernel/crates/rbpf/examples/helper.rs delete mode 100644 kernel/crates/rbpf/examples/load_elf.rs delete mode 100644 kernel/crates/rbpf/examples/load_elf__block_a_port.c delete mode 100644 kernel/crates/rbpf/examples/rbpf_plugin.rs delete mode 100644 kernel/crates/rbpf/examples/to_json.rs delete mode 100644 kernel/crates/rbpf/examples/uptime.rs delete mode 100644 kernel/crates/rbpf/mk/appveyor.bat delete mode 100644 kernel/crates/rbpf/rustfmt.toml delete mode 100644 kernel/crates/rbpf/src/asm_parser.rs delete mode 100644 kernel/crates/rbpf/src/assembler.rs delete mode 100644 kernel/crates/rbpf/src/cranelift.rs delete mode 100644 kernel/crates/rbpf/src/disassembler.rs delete mode 100644 kernel/crates/rbpf/src/ebpf.rs delete mode 100644 kernel/crates/rbpf/src/helpers.rs delete mode 100644 kernel/crates/rbpf/src/insn_builder.rs delete mode 100644 kernel/crates/rbpf/src/interpreter.rs delete mode 100644 kernel/crates/rbpf/src/jit.rs delete mode 100644 kernel/crates/rbpf/src/lib.rs delete mode 100644 kernel/crates/rbpf/src/no_std_error.rs delete mode 100644 kernel/crates/rbpf/src/stack.rs delete mode 100644 kernel/crates/rbpf/src/verifier.rs delete mode 100644 kernel/crates/rbpf/tests/assembler.rs delete mode 100644 kernel/crates/rbpf/tests/common.rs delete mode 100644 kernel/crates/rbpf/tests/cranelift.rs delete mode 100644 kernel/crates/rbpf/tests/disassembler.rs delete mode 100644 kernel/crates/rbpf/tests/misc.rs delete mode 100644 kernel/crates/rbpf/tests/ubpf_jit_x86_64.rs delete mode 100644 kernel/crates/rbpf/tests/ubpf_verifier.rs delete mode 100644 kernel/crates/rbpf/tests/ubpf_vm.rs diff --git a/.github/workflows/makefile.yml b/.github/workflows/makefile.yml index 362f91aa..1050bd12 100644 --- a/.github/workflows/makefile.yml +++ b/.github/workflows/makefile.yml @@ -51,7 +51,7 @@ jobs: env: ARCH: ${{ matrix.arch }} HOME: /root - run: bash -c "source /root/.cargo/env && cd kernel && make test && make test-rbpf" + run: bash -c "source /root/.cargo/env && cd kernel && make test" build: name: Build ${{ matrix.arch }} diff --git a/kernel/Cargo.lock b/kernel/Cargo.lock index 6213178f..4440dc6e 100644 --- a/kernel/Cargo.lock +++ b/kernel/Cargo.lock @@ -39,18 +39,6 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" -[[package]] -name = "anyhow" -version = "1.0.97" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" - -[[package]] -name = "arbitrary" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" - [[package]] name = "asm_macros" version = "0.1.0" @@ -83,12 +71,6 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" -[[package]] -name = "base-x" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" - [[package]] name = "bindgen" version = "0.61.0" @@ -144,30 +126,12 @@ checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" name = "bitmap" version = "0.1.0" -[[package]] -name = "bumpalo" -version = "3.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" - -[[package]] -name = "byteorder" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855" - [[package]] name = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" -[[package]] -name = "bytes" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" - [[package]] name = "cc" version = "1.2.17" @@ -221,136 +185,9 @@ version = "4.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" dependencies = [ - "bytes", "memchr", ] -[[package]] -name = "const_fn" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f8a2ca5ac02d09563609681103aada9e1777d54fc57a5acd7a41404f9c93b6e" - -[[package]] -name = "cranelift-bforest" -version = "0.99.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a91a1ccf6fb772808742db2f51e2179f25b1ec559cbe39ea080c72ff61caf8f" -dependencies = [ - "cranelift-entity", -] - -[[package]] -name = "cranelift-codegen" -version = "0.99.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "169db1a457791bff4fd1fc585bb5cc515609647e0420a7d5c98d7700c59c2d00" -dependencies = [ - "bumpalo", - "cranelift-bforest", - "cranelift-codegen-meta", - "cranelift-codegen-shared", - "cranelift-control", - "cranelift-entity", - "cranelift-isle", - "gimli 0.27.3", - "hashbrown 0.13.2", - "log", - "regalloc2", - "smallvec", - "target-lexicon", -] - -[[package]] -name = "cranelift-codegen-meta" -version = "0.99.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3486b93751ef19e6d6eef66d2c0e83ed3d2ba01da1919ed2747f2f7bd8ba3419" -dependencies = [ - "cranelift-codegen-shared", -] - -[[package]] -name = "cranelift-codegen-shared" -version = "0.99.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86a1205ab18e7cd25dc4eca5246e56b506ced3feb8d95a8d776195e48d2cd4ef" - -[[package]] -name = "cranelift-control" -version = "0.99.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b108cae0f724ddfdec1871a0dc193a607e0c2d960f083cfefaae8ccf655eff2" -dependencies = [ - "arbitrary", -] - -[[package]] -name = "cranelift-entity" -version = "0.99.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "720444006240622798665bfc6aa8178e2eed556da342fda62f659c5267c3c659" - -[[package]] -name = "cranelift-frontend" -version = "0.99.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7a94c4c5508b7407e125af9d5320694b7423322e59a4ac0d07919ae254347ca" -dependencies = [ - "cranelift-codegen", - "log", - "smallvec", - "target-lexicon", -] - -[[package]] -name = "cranelift-isle" -version = "0.99.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef1f888d0845dcd6be4d625b91d9d8308f3d95bed5c5d4072ce38e1917faa505" - -[[package]] -name = "cranelift-jit" -version = "0.99.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "547845cd12d15167e5458556ee67513bfaff2e05e72eb489edfbabc9f21d9ea2" -dependencies = [ - "anyhow", - "cranelift-codegen", - "cranelift-control", - "cranelift-entity", - "cranelift-module", - "cranelift-native", - "libc", - "log", - "region", - "target-lexicon", - "wasmtime-jit-icache-coherence", - "windows-sys 0.48.0", -] - -[[package]] -name = "cranelift-module" -version = "0.99.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c104ed6a4c56c15e1858cc466482373e3c13d022bc1391485769ba384d9b079" -dependencies = [ - "anyhow", - "cranelift-codegen", - "cranelift-control", -] - -[[package]] -name = "cranelift-native" -version = "0.99.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ad5966da08f1e96a3ae63be49966a85c9b249fa465f8cf1b66469a82b1004a0" -dependencies = [ - "cranelift-codegen", - "libc", - "target-lexicon", -] - [[package]] name = "crc" version = "0.1.0" @@ -476,12 +313,6 @@ dependencies = [ "syn 2.0.100", ] -[[package]] -name = "discard" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" - [[package]] name = "doc-comment" version = "0.3.3" @@ -503,7 +334,7 @@ dependencies = [ "defer", "derive_builder", "driver_base_macros", - "elf 0.7.2", + "elf", "fdt", "hashbrown 0.13.2", "ida", @@ -553,15 +384,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" -[[package]] -name = "elf" -version = "0.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4841de15dbe0e49b9b62a417589299e3be0d557e0900d36acb87e6dae47197f5" -dependencies = [ - "byteorder 0.5.3", -] - [[package]] name = "elf" version = "0.7.2" @@ -600,15 +422,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys", ] -[[package]] -name = "fallible-iterator" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" - [[package]] name = "fdt" version = "0.2.0-alpha1" @@ -637,17 +453,6 @@ dependencies = [ "wasi", ] -[[package]] -name = "gimli" -version = "0.27.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" -dependencies = [ - "fallible-iterator", - "indexmap 1.9.3", - "stable_deref_trait", -] - [[package]] name = "gimli" version = "0.31.1" @@ -666,15 +471,9 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" dependencies = [ - "byteorder 1.5.0", + "byteorder", ] -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - [[package]] name = "hashbrown" version = "0.13.2" @@ -714,19 +513,13 @@ dependencies = [ "libc", ] -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - [[package]] name = "home" version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" dependencies = [ - "windows-sys 0.59.0", + "windows-sys", ] [[package]] @@ -748,16 +541,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", -] - [[package]] name = "indexmap" version = "2.8.0" @@ -817,12 +600,6 @@ dependencies = [ "libc", ] -[[package]] -name = "json" -version = "0.11.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92c245af8786f6ac35f95ca14feca9119e71339aaab41e878e7cdd655c97e9e5" - [[package]] name = "kcmdline_macros" version = "0.1.0" @@ -956,15 +733,6 @@ dependencies = [ "hashbrown 0.15.2", ] -[[package]] -name = "mach" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" -dependencies = [ - "libc", -] - [[package]] name = "mach2" version = "0.4.2" @@ -1192,12 +960,6 @@ dependencies = [ "syn 2.0.100", ] -[[package]] -name = "proc-macro-hack" -version = "0.5.20+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" - [[package]] name = "proc-macro2" version = "1.0.94" @@ -1286,34 +1048,13 @@ dependencies = [ [[package]] name = "rbpf" -version = "0.2.0" +version = "0.3.0" +source = "git+https://git.mirrors.dragonos.org.cn/DragonOS-Community/rbpf?rev=f31e471a29#f31e471a29dea9a0c272626f7b762adba755e6cc" dependencies = [ - "byteorder 1.5.0", + "byteorder", "combine", - "cranelift-codegen", - "cranelift-frontend", - "cranelift-jit", - "cranelift-module", - "cranelift-native", - "elf 0.0.10", - "hex", - "json", - "libc", + "hashbrown 0.15.2", "log", - "time", -] - -[[package]] -name = "regalloc2" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad156d539c879b7a24a363a2016d77961786e71f48f2e2fc8302a92abd2429a6" -dependencies = [ - "hashbrown 0.13.2", - "log", - "rustc-hash", - "slice-group-by", - "smallvec", ] [[package]] @@ -1345,18 +1086,6 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" -[[package]] -name = "region" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877e54ea2adcd70d80e9179344c97f93ef0dffd6b03e1f4529e6e83ab2fa9ae0" -dependencies = [ - "bitflags 1.3.2", - "libc", - "mach", - "winapi", -] - [[package]] name = "ringbuffer" version = "0.15.0" @@ -1378,15 +1107,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver", -] - [[package]] name = "rustix" version = "0.38.44" @@ -1397,7 +1117,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.59.0", + "windows-sys", ] [[package]] @@ -1433,21 +1153,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - [[package]] name = "serde" version = "1.0.219" @@ -1489,21 +1194,6 @@ dependencies = [ "serde", ] -[[package]] -name = "sha1" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770" -dependencies = [ - "sha1_smol", -] - -[[package]] -name = "sha1_smol" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" - [[package]] name = "shlex" version = "1.3.0" @@ -1520,12 +1210,6 @@ dependencies = [ "spin 0.9.8", ] -[[package]] -name = "slice-group-by" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" - [[package]] name = "smallvec" version = "1.14.0" @@ -1539,7 +1223,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a1a996951e50b5971a2c8c0fa05a381480d70a933064245c4a223ddc87ccc97" dependencies = [ "bitflags 1.3.2", - "byteorder 1.5.0", + "byteorder", "cfg-if", "defmt", "heapless", @@ -1567,15 +1251,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" -[[package]] -name = "standback" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff" -dependencies = [ - "version_check", -] - [[package]] name = "static-keys" version = "0.7.0" @@ -1588,55 +1263,6 @@ dependencies = [ "windows 0.59.0", ] -[[package]] -name = "stdweb" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" -dependencies = [ - "discard", - "rustc_version", - "stdweb-derive", - "stdweb-internal-macros", - "stdweb-internal-runtime", - "wasm-bindgen", -] - -[[package]] -name = "stdweb-derive" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" -dependencies = [ - "proc-macro2", - "quote", - "serde", - "serde_derive", - "syn 1.0.109", -] - -[[package]] -name = "stdweb-internal-macros" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" -dependencies = [ - "base-x", - "proc-macro2", - "quote", - "serde", - "serde_derive", - "serde_json", - "sha1", - "syn 1.0.109", -] - -[[package]] -name = "stdweb-internal-runtime" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" - [[package]] name = "strsim" version = "0.11.1" @@ -1677,12 +1303,6 @@ dependencies = [ "num-traits 0.2.15", ] -[[package]] -name = "target-lexicon" -version = "0.12.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" - [[package]] name = "target-triple" version = "0.1.4" @@ -1727,44 +1347,6 @@ dependencies = [ "syn 2.0.100", ] -[[package]] -name = "time" -version = "0.2.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4752a97f8eebd6854ff91f1c1824cd6160626ac4bd44287f7f4ea2035a02a242" -dependencies = [ - "const_fn", - "libc", - "standback", - "stdweb", - "time-macros", - "version_check", - "winapi", -] - -[[package]] -name = "time-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1" -dependencies = [ - "proc-macro-hack", - "time-macros-impl", -] - -[[package]] -name = "time-macros-impl" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3c141a1b43194f3f56a1411225df8646c55781d5f26db825b3d98507eb482f" -dependencies = [ - "proc-macro-hack", - "proc-macro2", - "quote", - "standback", - "syn 1.0.109", -] - [[package]] name = "toml" version = "0.8.20" @@ -1792,7 +1374,7 @@ version = "0.22.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" dependencies = [ - "indexmap 2.8.0", + "indexmap", "serde", "serde_spanned", "toml_datetime", @@ -1900,7 +1482,7 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "637d511437df708cee34bdec7ba2f1548d256b7acf3ff20e0a1c559f9bf3a987" dependencies = [ - "gimli 0.31.1", + "gimli", ] [[package]] @@ -1944,75 +1526,6 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" -[[package]] -name = "wasm-bindgen" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn 2.0.100", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.100", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "wasmtime-jit-icache-coherence" -version = "12.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b59f94b0409221873565419168e20b5aedf18c4bd64de5c38acf8f0634efeee3" -dependencies = [ - "cfg-if", - "libc", - "windows-sys 0.48.0", -] - [[package]] name = "which" version = "4.4.2" @@ -2047,7 +1560,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.59.0", + "windows-sys", ] [[package]] @@ -2189,15 +1702,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - [[package]] name = "windows-sys" version = "0.59.0" @@ -2207,21 +1711,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - [[package]] name = "windows-targets" version = "0.52.6" @@ -2254,12 +1743,6 @@ dependencies = [ "windows_x86_64_msvc 0.53.0", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -2272,12 +1755,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -2290,12 +1767,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -2320,12 +1791,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -2338,12 +1803,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -2356,12 +1815,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -2374,12 +1827,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -2458,7 +1905,7 @@ version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ - "byteorder 1.5.0", + "byteorder", "zerocopy-derive 0.7.35", ] diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 1b7c4152..e20aaa81 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -75,7 +75,7 @@ log = "0.4.21" kprobe = { path = "crates/kprobe" } lru = "0.12.3" -rbpf = { path = "crates/rbpf" } +rbpf = { git = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/rbpf", rev = "f31e471a29", default-features = false } printf-compat = { git = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/printf-compat", rev = "5f5c9cc363", default-features = false } static-keys = { version = "=0.7" } diff --git a/kernel/Makefile b/kernel/Makefile index 9c71c07f..2a95044f 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -41,7 +41,4 @@ check: ECHO test: # 测试内核库 - RUSTFLAGS="$(RUSTFLAGS)" cargo +nightly-2024-11-05 test --workspace --exclude dragonos_kernel rbpf - -test-rbpf: - cd crates/rbpf && RUSTFLAGS="$(RUSTFLAGS)" cargo +nightly-2024-11-05 test --features=std,user,cranelift \ No newline at end of file + RUSTFLAGS="$(RUSTFLAGS)" cargo +nightly-2024-11-05 test --workspace --exclude dragonos_kernel diff --git a/kernel/crates/rbpf/.appveyor.yml b/kernel/crates/rbpf/.appveyor.yml deleted file mode 100644 index c8ea114a..00000000 --- a/kernel/crates/rbpf/.appveyor.yml +++ /dev/null @@ -1,21 +0,0 @@ -version: 1.0.{build} -branches: - only: - - main -os: - - Visual Studio 2015 -clone_depth: 1 -configuration: - - Debug -platform: - - x64 -environment: - matrix: - - TOOLCHAIN_VERSION: 14.0 - RUST: 1.76.0 - - TOOLCHAIN_VERSION: 14.0 - RUST: beta - - TOOLCHAIN_VERSION: 14.0 - RUST: nightly - -build_script: mk/appveyor.bat diff --git a/kernel/crates/rbpf/.gitignore b/kernel/crates/rbpf/.gitignore deleted file mode 100644 index a9d37c56..00000000 --- a/kernel/crates/rbpf/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -target -Cargo.lock diff --git a/kernel/crates/rbpf/Cargo.toml b/kernel/crates/rbpf/Cargo.toml deleted file mode 100644 index 1a711ac9..00000000 --- a/kernel/crates/rbpf/Cargo.toml +++ /dev/null @@ -1,78 +0,0 @@ -[package] - -# Project metadata -name = "rbpf" -version = "0.2.0" -authors = ["Quentin "] - -# Additional metadata for packaging -description = "Virtual machine and JIT compiler for eBPF programs" -repository = "https://github.com/qmonnet/rbpf" -readme = "README.md" -keywords = ["BPF", "eBPF", "interpreter", "JIT", "filtering"] -license = "Apache-2.0/MIT" -edition = "2021" - -# Packaging directives -include = [ - "src/**", - "examples/**", - "tests/**", - "bench/**", - "LICENSE*", - "Cargo.toml", -] - -[dependencies] - -# Default features (std) are disabled so that the dependencies don't pull in the -# standard library when the crate is compiled for no_std -byteorder = { version = "1.2", default-features = false } -log = {version = "0.4.21", default-features = false } -combine = { version = "4.6", default-features = false } - -# Optional Dependencies when using the standard library -libc = { version = "0.2", optional = true } -time = { version = "0.2", optional = true } - -# Optional Dependencies for the CraneLift JIT -cranelift-codegen = { version = "0.99", optional = true } -cranelift-frontend = { version = "0.99", optional = true } -cranelift-jit = { version = "0.99", optional = true } -cranelift-native = { version = "0.99", optional = true } -cranelift-module = { version = "0.99", optional = true } - -[dev-dependencies] - -elf = "0.0.10" -json = "0.11" -hex = "0.4.3" - -[features] -#default = ["std", "user", "cranelift"] -cargo-clippy = [] -std = ["dep:time", "dep:libc", "combine/std"] -cranelift = [ - "dep:cranelift-codegen", - "dep:cranelift-frontend", - "dep:cranelift-jit", - "dep:cranelift-native", - "dep:cranelift-module", -] -user = [] - -# Examples that depend on the standard library should be disabled when -# testing the `no_std` configuration. -[[example]] -name = "disassemble" -required-features = ["std"] - -[[example]] -name = "uptime" -required-features = ["std"] - -[[example]] -name = "to_json" - -[[example]] -name = "rbpf_plugin" diff --git a/kernel/crates/rbpf/LICENSE-APACHE b/kernel/crates/rbpf/LICENSE-APACHE deleted file mode 100644 index d6456956..00000000 --- a/kernel/crates/rbpf/LICENSE-APACHE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/kernel/crates/rbpf/LICENSE-MIT b/kernel/crates/rbpf/LICENSE-MIT deleted file mode 100644 index 661a705a..00000000 --- a/kernel/crates/rbpf/LICENSE-MIT +++ /dev/null @@ -1,25 +0,0 @@ -Copyright (c) 2016 6WIND S.A. - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/kernel/crates/rbpf/README.md b/kernel/crates/rbpf/README.md deleted file mode 100644 index e2dc8ce7..00000000 --- a/kernel/crates/rbpf/README.md +++ /dev/null @@ -1,743 +0,0 @@ -# rbpf - - - - - - -Rust (user-space) virtual machine for eBPF - -[![Build Status](https://github.com/qmonnet/rbpf/actions/workflows/test.yaml/badge.svg)](https://github.com/qmonnet/rbpf/actions/workflows/test.yaml) -[![Build status](https://ci.appveyor.com/api/projects/status/ia74coeuhxtrcvsk/branch/main?svg=true)](https://ci.appveyor.com/project/qmonnet/rbpf/branch/main) -[![Coverage Status](https://coveralls.io/repos/github/qmonnet/rbpf/badge.svg?branch=main)](https://coveralls.io/github/qmonnet/rbpf?branch=main) -[![Crates.io](https://img.shields.io/crates/v/rbpf.svg)](https://crates.io/crates/rbpf) - -* [Description](#description) -* [Link to the crate](#link-to-the-crate) -* [API](#api) -* [Example uses](#example-uses) -* [Building eBPF programs](#building-ebpf-programs) -* [Build Features](#build-features) -* [Feedback welcome!](#feedback-welcome) -* [Questions / Answers](#questions--answers) -* [Caveats](#caveats) -* [_To do_ list](#to-do-list) -* [License](#license) -* [Inspired by](#inspired-by) -* [Other resources](#other-resources) - -## Description - -This crate contains a virtual machine for eBPF program execution. BPF, as in -_Berkeley Packet Filter_, is an assembly-like language initially developed for -BSD systems, in order to filter packets in the kernel with tools such as -tcpdump so as to avoid useless copies to user-space. It was ported to Linux, -where it evolved into eBPF (_extended_ BPF), a faster version with more -features. While BPF programs are originally intended to run in the kernel, the -virtual machine of this crate enables running it in user-space applications; -it contains an interpreter, an x86_64 JIT-compiler for eBPF programs, as well as -a disassembler. - -It is based on Rich Lane's [uBPF software](https://github.com/iovisor/ubpf/), -which does nearly the same, but is written in C. - -The crate is supposed to compile and run on Linux, MacOS X, and Windows, -although the JIT-compiler does not work with Windows at this time. - -## Link to the crate - -This crate is available from [crates.io](https://crates.io/crates/rbpf), so it -should work out of the box by adding it as a dependency in your `Cargo.toml` -file: - -```toml -[dependencies] -rbpf = "0.2.0" -``` - -You can also use the development version from this GitHub repository. This -should be as simple as putting this inside your `Cargo.toml`: - -```toml -[dependencies] -rbpf = { git = "https://github.com/qmonnet/rbpf" } -``` - -Of course, if you prefer, you can clone it locally, possibly hack the crate, -and then indicate the path of your local version in `Cargo.toml`: - -```toml -[dependencies] -rbpf = { path = "path/to/rbpf" } -``` - -Then indicate in your source code that you want to use the crate: - -```rust,ignore -extern crate rbpf; -``` - -## API - -The API is pretty well documented inside the source code. You should also be -able to access [an online version of the documentation from -here](https://docs.rs/rbpf/), automatically generated from the -[crates.io](https://crates.io/crates/rbpf) version (may not be up-to-date with -the main branch). [Examples](../../tree/main/examples) and [unit -tests](../../tree/main/tests) should also prove helpful. Here is a summary of -how to use the crate. - -Here are the steps to follow to run an eBPF program with rbpf: - -1. Create a virtual machine. There are several kinds of machines, we will come - back on this later. When creating the VM, pass the eBPF program as an - argument to the constructor. -2. If you want to use some helper functions, register them into the virtual - machine. -3. If you want a JIT-compiled program, compile it. -4. Execute your program: either run the interpreter or call the JIT-compiled - function. - -eBPF has been initially designed to filter packets (now it has some other hooks -in the Linux kernel, such as kprobes, but this is not covered by rbpf). As a -consequence, most of the load and store instructions of the program are -performed on a memory area representing the packet data. However, in the Linux -kernel, the eBPF program does not immediately access this data area: initially, -it has access to a C `struct sk_buff` instead, which is a buffer containing -metadata about the packet—including memory addresses of the beginning and of -the end of the packet data area. So the program first loads those pointers from -the `sk_buff`, and then can access the packet data. - -This behavior can be replicated with rbpf, but it is not mandatory. For this -reason, we have several structs representing different kinds of virtual -machines: - -* `struct EbpfVmMbuffer` mimics the kernel. When the program is run, the - address provided to its first eBPF register will be the address of a metadata - buffer provided by the user, and that is expected to contain pointers to the - start and the end of the packet data memory area. - -* `struct EbpfVmFixedMbuff` has one purpose: enabling the execution of programs - created to be compatible with the kernel, while saving the effort to manually - handle the metadata buffer for the user. In fact, this struct has a static - internal buffer that is passed to the program. The user has to indicate the - offset values at which the eBPF program expects to find the start and the end - of packet data in the buffer. On calling the function that runs the program - (JITted or not), the struct automatically updates the addresses in this - static buffer, at the appointed offsets, for the start and the end of the - packet data the program is called upon. - -* `struct EbpfVmRaw` is for programs that want to run directly on packet data. - No metadata buffer is involved, the eBPF program directly receives the - address of the packet data in its first register. This is the behavior of - uBPF. - -* `struct EbpfVmNoData` does not take any data. The eBPF program takes no - argument whatsoever and its return value is deterministic. Not so sure there - is a valid use case for that, but if nothing else, this is very useful for - unit tests. - -All these structs implement the same public functions: - -```rust,ignore -// called with EbpfVmMbuff:: prefix -pub fn new(prog: &'a [u8]) -> Result, Error> - -// called with EbpfVmFixedMbuff:: prefix -pub fn new(prog: &'a [u8], - data_offset: usize, - data_end_offset: usize) -> Result, Error> - -// called with EbpfVmRaw:: prefix -pub fn new(prog: &'a [u8]) -> Result, Error> - -// called with EbpfVmNoData:: prefix -pub fn new(prog: &'a [u8]) -> Result, Error> -``` - -This is used to create a new instance of a VM. The return type is dependent of -the struct from which the function is called. For instance, -`rbpf::EbpfVmRaw::new(Some(my_program))` would return an instance of `struct -rbpf::EbpfVmRaw` (wrapped in a `Result`). When a program is loaded, it is -checked with a very simple verifier (nothing close to the one for Linux -kernel). Users are also able to replace it with a custom verifier. - -For `struct EbpfVmFixedMbuff`, two additional arguments must be passed to the -constructor: `data_offset` and `data_end_offset`. They are the offset (byte -number) at which the pointers to the beginning and to the end, respectively, of -the memory area of packet data are to be stored in the internal metadata buffer -each time the program is executed. Other structs do not use this mechanism and -do not need those offsets. - -```rust,ignore -// for struct EbpfVmMbuff, struct EbpfVmRaw and struct EbpfVmRawData -pub fn set_program(&mut self, prog: &'a [u8]) -> Result<(), Error> - -// for struct EbpfVmFixedMbuff -pub fn set_program(&mut self, prog: &'a [u8], - data_offset: usize, - data_end_offset: usize) -> Result<(), Error> -``` - -You can use for example `my_vm.set_program(my_program);` to change the loaded -program after the VM instance creation. This program is checked with the -verifier attached to the VM. The verifying function of the VM can be changed at -any moment. - -```rust,ignore -pub type Verifier = fn(prog: &[u8]) -> Result<(), Error>; - -pub fn set_verifier(&mut self, - verifier: Verifier) -> Result<(), Error> -``` - -Note that if a program has already been loaded into the VM, setting a new -verifier also immediately runs it on the loaded program. However, the verifier -is not run if no program has been loaded (if `None` was passed to the `new()` -method when creating the VM). - -```rust,ignore -pub type Helper = fn (u64, u64, u64, u64, u64) -> u64; - -pub fn register_helper(&mut self, - key: u32, - function: Helper) -> Result<(), Error> -``` - -This function is used to register a helper function. The VM stores its -registers in a hashmap, so the key can be any `u32` value you want. It may be -useful for programs that should be compatible with the Linux kernel and -therefore must use specific helper numbers. - -```rust,ignore -// for struct EbpfVmMbuff -pub fn execute_program(&self, - mem: &'a mut [u8], - mbuff: &'a mut [u8]) -> Result<(u64), Error> - -// for struct EbpfVmFixedMbuff and struct EbpfVmRaw -pub fn execute_program(&self, - mem: &'a mut [u8]) -> Result<(u64), Error> - -// for struct EbpfVmNoData -pub fn execute_program(&self) -> Result<(u64), Error> -``` - -Interprets the loaded program. The function takes a reference to the packet -data and the metadata buffer, or only to the packet data, or nothing at all, -depending on the kind of the VM used. The value returned is the result of the -eBPF program. - -```rust,ignore -pub fn jit_compile(&mut self) -> Result<(), Error> -``` - -JIT-compile the loaded program, for x86_64 architecture. If the program is to -use helper functions, they must be registered into the VM before this function -is called. The generated assembly function is internally stored in the VM. - -```rust,ignore -// for struct EbpfVmMbuff -pub unsafe fn execute_program_jit(&self, mem: &'a mut [u8], - mbuff: &'a mut [u8]) -> Result<(u64), Error> - -// for struct EbpfVmFixedMbuff and struct EbpfVmRaw -pub unsafe fn execute_program_jit(&self, mem: &'a mut [u8]) -> Result<(u64), Error> - -// for struct EbpfVmNoData -pub unsafe fn execute_program_jit(&self) -> Result<(u64), Error> -``` - -Calls the JIT-compiled program. The arguments to provide are the same as for -`execute_program()`, again depending on the kind of VM that is used. The result of -the JIT-compiled program should be the same as with the interpreter, but it -should run faster. Note that if errors occur during the program execution, the -JIT-compiled version does not handle it as well as the interpreter, and the -program may crash. For this reason, the functions are marked as `unsafe`. - -## Example uses - -### Simple example - -This comes from the unit test `test_vm_add`. - -```rust -extern crate rbpf; - -fn main() { - - // This is the eBPF program, in the form of bytecode instructions. - let prog = &[ - 0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov32 r0, 0 - 0xb4, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, // mov32 r1, 2 - 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // add32 r0, 1 - 0x0c, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // add32 r0, r1 - 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit - ]; - - // Instantiate a struct EbpfVmNoData. This is an eBPF VM for programs that - // takes no packet data in argument. - // The eBPF program is passed to the constructor. - let vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap(); - - // Execute (interpret) the program. No argument required for this VM. - assert_eq!(vm.execute_program().unwrap(), 0x3); -} -``` - -### With JIT, on packet data - -This comes from the unit test `test_jit_ldxh`. - -```rust -extern crate rbpf; - -fn main() { - let prog = &[ - 0x71, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxh r0, [r1+2] - 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit - ]; - - // Let's use some data. - let mem = &mut [ - 0xaa, 0xbb, 0x11, 0xcc, 0xdd - ]; - - // This is an eBPF VM for programs reading from a given memory area (it - // directly reads from packet data) - let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap(); - - #[cfg(any(windows, not(feature = "std")))] { - assert_eq!(vm.execute_program(mem).unwrap(), 0x11); - } - #[cfg(all(not(windows), feature = "std"))] { - // This time we JIT-compile the program. - vm.jit_compile().unwrap(); - - // Then we execute it. For this kind of VM, a reference to the packet - // data must be passed to the function that executes the program. - unsafe { assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x11); } - } -} -``` -### Using a metadata buffer - -This comes from the unit test `test_jit_mbuff` and derives from the unit test -`test_jit_ldxh`. - -```rust -extern crate rbpf; - -fn main() { - let prog = &[ - // Load mem from mbuff at offset 8 into R1 - 0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, - // ldhx r1[2], r0 - 0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - ]; - let mem = &mut [ - 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd - ]; - - // Just for the example we create our metadata buffer from scratch, and - // we store the pointers to packet data start and end in it. - let mut mbuff = &mut [0u8; 32]; - unsafe { - let mut data = mbuff.as_ptr().offset(8) as *mut u64; - let mut data_end = mbuff.as_ptr().offset(24) as *mut u64; - *data = mem.as_ptr() as u64; - *data_end = mem.as_ptr() as u64 + mem.len() as u64; - } - - // This eBPF VM is for program that use a metadata buffer. - let mut vm = rbpf::EbpfVmMbuff::new(Some(prog)).unwrap(); - - #[cfg(any(windows, not(feature = "std")))] { - assert_eq!(vm.execute_program(mem, mbuff).unwrap(), 0x2211); - } - #[cfg(all(not(windows), feature = "std"))] { - // Here again we JIT-compile the program. - vm.jit_compile().unwrap(); - - // Here we must provide both a reference to the packet data, and to the - // metadata buffer we use. - unsafe { - assert_eq!(vm.execute_program_jit(mem, mbuff).unwrap(), 0x2211); - } - } -} -``` - -### Loading code from an object file; and using a virtual metadata buffer - -This comes from unit test `test_vm_block_port`. - -This example requires the following additional crates, you may have to add them -to your `Cargo.toml` file. - -```toml -[dependencies] -rbpf = "0.2.0" -elf = "0.0.10" -``` - -It also uses a kind of VM that uses an internal buffer used to simulate the -`sk_buff` used by eBPF programs in the kernel, without having to manually -create a new buffer for each packet. It may be useful for programs compiled for -the kernel and that assumes the data they receive is a `sk_buff` pointing to -the packet data start and end addresses. So here we just provide the offsets at -which the eBPF program expects to find those pointers, and the VM handles the -buffer update so that we only have to provide a reference to the packet data -for each run of the program. - -```rust -extern crate elf; -use std::path::PathBuf; - -extern crate rbpf; -use rbpf::helpers; - -fn main() { - // Load a program from an ELF file, e.g. compiled from C to eBPF with - // clang/LLVM. Some minor modification to the bytecode may be required. - let filename = "examples/load_elf__block_a_port.elf"; - - let path = PathBuf::from(filename); - let file = match elf::File::open_path(&path) { - Ok(f) => f, - Err(e) => panic!("Error: {:?}", e), - }; - - // Here we assume the eBPF program is in the ELF section called - // ".classifier". - let text_scn = match file.get_section(".classifier") { - Some(s) => s, - None => panic!("Failed to look up .classifier section"), - }; - - let prog = &text_scn.data; - - // This is our data: a real packet, starting with Ethernet header - let packet = &mut [ - 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, - 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, - 0x08, 0x00, // ethertype - 0x45, 0x00, 0x00, 0x3b, // start ip_hdr - 0xa6, 0xab, 0x40, 0x00, - 0x40, 0x06, 0x96, 0x0f, - 0x7f, 0x00, 0x00, 0x01, - 0x7f, 0x00, 0x00, 0x01, - 0x99, 0x99, 0xc6, 0xcc, // start tcp_hdr - 0xd1, 0xe5, 0xc4, 0x9d, - 0xd4, 0x30, 0xb5, 0xd2, - 0x80, 0x18, 0x01, 0x56, - 0xfe, 0x2f, 0x00, 0x00, - 0x01, 0x01, 0x08, 0x0a, // start data - 0x00, 0x23, 0x75, 0x89, - 0x00, 0x23, 0x63, 0x2d, - 0x71, 0x64, 0x66, 0x73, - 0x64, 0x66, 0x0a - ]; - - // This is an eBPF VM for programs using a virtual metadata buffer, similar - // to the sk_buff that eBPF programs use with tc and in Linux kernel. - // We must provide the offsets at which the pointers to packet data start - // and end must be stored: these are the offsets at which the program will - // load the packet data from the metadata buffer. - let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap(); - - // We register a helper function, that can be called by the program, into - // the VM. The `bpf_trace_printf` is only available when we have access to - // the standard library. - #[cfg(feature = "std")] { - vm.register_helper(helpers::BPF_TRACE_PRINTK_IDX, - helpers::bpf_trace_printf).unwrap(); - } - - // This kind of VM takes a reference to the packet data, but does not need - // any reference to the metadata buffer: a fixed buffer is handled - // internally by the VM. - let res = vm.execute_program(packet).unwrap(); - println!("Program returned: {:?} ({:#x})", res, res); -} -``` - -## Building eBPF programs - -Besides passing the raw hexadecimal codes for building eBPF programs, two other -methods are available. - -### Assembler - -The first method consists in using the assembler provided by the crate. - -```rust -extern crate rbpf; -use rbpf::assembler::assemble; - -let prog = assemble("add64 r1, 0x605 - mov64 r2, 0x32 - mov64 r1, r0 - be16 r0 - neg64 r2 - exit").unwrap(); - -#[cfg(feature = "std")] { - println!("{:?}", prog); -} -``` - -The above snippet will produce: - -```rust,ignore -Ok([0x07, 0x01, 0x00, 0x00, 0x05, 0x06, 0x00, 0x00, - 0xb7, 0x02, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, - 0xbf, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x87, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) -``` - -Conversely, a disassembler is also available to dump instruction names from -bytecode in a human-friendly format. - -```rust -extern crate rbpf; -use rbpf::disassembler::disassemble; - -let prog = &[ - 0x07, 0x01, 0x00, 0x00, 0x05, 0x06, 0x00, 0x00, - 0xb7, 0x02, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, - 0xbf, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x87, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -]; - -disassemble(prog); -``` - -This will produce the following output: - -```txt -add64 r1, 0x605 -mov64 r2, 0x32 -mov64 r1, r0 -be16 r0 -neg64 r2 -exit -``` - -Please refer to [source code](src/assembler.rs) and [tests](tests/assembler.rs) -for the syntax and the list of instruction names. - -### Building API - -The other way to build programs is to chain commands from the instruction -builder API. It looks less like assembly, maybe more like high-level functions. -What's sure is that the result is more verbose, but if you prefer to build -programs this way, it works just as well. If we take again the same sample as -above, it would be constructed as follows. - -```rust -extern crate rbpf; -use rbpf::insn_builder::*; - -let mut program = BpfCode::new(); -program.add(Source::Imm, Arch::X64).set_dst(1).set_imm(0x605).push() - .mov(Source::Imm, Arch::X64).set_dst(2).set_imm(0x32).push() - .mov(Source::Reg, Arch::X64).set_src(0).set_dst(1).push() - .swap_bytes(Endian::Big).set_dst(0).set_imm(0x10).push() - .negate(Arch::X64).set_dst(2).push() - .exit().push(); -``` - -Again, please refer to [the source and related tests](src/insn_builder.rs) to -get more information and examples on how to use it. - -## Build features - -### `no_std` - -The `rbpf` crate has a Cargo feature named "std" that is enabled by default. To -use `rbpf` in `no_std` environments this feature needs to be disabled. To do -this, you need to modify your dependency on `rbpf` in Cargo.toml to disable the -enabled-by-default features. - -```toml -[dependencies] -rbpf = { version = "1.0", default-features = false } -``` - -Note that when using this crate in `no_std` environments, the `jit` module -isn't available. This is because it depends on functions provided by `libc` -(`libc::posix_memalign()`, `libc::mprotect()`) which aren't available on -`no_std`. - -The `assembler` module is available, albeit with reduced debugging features. It -depends on the `combine` crate providing parser combinators. Under `no_std` -this crate only provides simple parsers which generate less descriptive error -messages. - -## Feedback welcome! - -This is the author's first try at writing Rust code. He learned a lot in the -process, but there remains a feeling that this crate has a kind of C-ish style -in some places instead of the Rusty look the author would like it to have. So -feedback (or PRs) are welcome, including about ways you might see to take -better advantage of Rust features. - -Note that the project expects new commits to be covered by the -[Developer's Certificate of Origin](https://wiki.linuxfoundation.org/dco). -When contributing Pull Requests, please sign off your commits accordingly. - -## Questions / Answers - -### Why implementing an eBPF virtual machine in Rust? - -As of this writing, there is no particular use case for this crate at the best -of the author's knowledge. The author happens to work with BPF on Linux and to -know how uBPF works, and he wanted to learn and experiment with Rust—no more -than that. - -### What are the differences with uBPF? - -Other than the language, obviously? Well, there are some differences: - -* Some constants, such as the maximum length for programs or the length for the - stack, differs between uBPF and rbpf. The latter uses the same values as the - Linux kernel, while uBPF has its own values. - -* When an error occurs while a program is run by uBPF, the function running the - program silently returns the maximum value as an error code, while rbpf - returns Rust type `Error`. - -* The registration of helper functions, that can be called from within an eBPF - program, is not handled in the same way. - -* The distinct structs permitting to run program either on packet data, or with - a metadata buffer (simulated or not) is a specificity of rbpf. - -* As for performance: theoretically the JITted programs are expected to run at - the same speed, while the C interpreter of uBPF should go slightly faster - than rbpf. But this has not been asserted yet. Benchmarking both programs - would be an interesting thing to do. - -### Can I use it with the “classic” BPF (a.k.a cBPF) version? - -No. This crate only works with extended BPF (eBPF) programs. For cBPF programs, -such as used by tcpdump (as of this writing) for example, you may be interested -in the [bpfjit crate](https://crates.io/crates/bpfjit) written by Alexander -Polakov instead. - -### What functionalities are implemented? - -Running and JIT-compiling eBPF programs work. There is also a mechanism to -register user-defined helper functions. The eBPF implementation of the Linux -kernel comes with [some additional -features](https://github.com/iovisor/bcc/blob/master/docs/kernel-versions.md): -a high number of helpers, several kinds of maps, tail calls. - -* Additional helpers should be easy to add, but very few of the existing Linux - helpers have been replicated in rbpf so far. - -* Tail calls (“long jumps” from an eBPF program into another) are not - implemented. This is probably not trivial to design and implement. - -* The interaction with maps is done through the use of specific helpers, so - this should not be difficult to add. The maps themselves can reuse the maps - in the kernel (if on Linux), to communicate with in-kernel eBPF programs for - instance; or they can be handled in user space. Rust has arrays and hashmaps, - so their implementation should be pretty straightforward (and may be added to - rbpf in the future). - -### What about program validation? - -The ”verifier” of this crate is very short and has nothing to do with the -kernel verifier, which means that it accepts programs that may not be safe. On -the other hand, you probably do not run this in a kernel here, so it will not -crash your system. Implementing a verifier similar to the one in the kernel is -not trivial, and we cannot “copy” it since it is under GPL license. - -### What about safety then? - -Rust has a strong emphasis on safety. Yet to have the eBPF VM work, some -`unsafe` blocks of code are used. The VM, taken as an eBPF interpreter, can -return an error but should not crash. Please file an issue otherwise. - -As for the JIT-compiler, it is a different story, since runtime memory checks -are more complicated to implement in assembly. It _will_ crash if your -JIT-compiled program tries to perform unauthorized memory accesses. Usually, it -could be a good idea to test your program with the interpreter first. - -Oh, and if your program has infinite loops, even with the interpreter, you're -on your own. - -## Caveats - -* This crate is **under development** and the API may be subject to change. - -* The JIT compiler produces an unsafe program: memory access are not tested at - runtime (yet). Use with caution. - -* A small number of eBPF instructions have not been implemented yet. This - should not be a problem for the majority of eBPF programs. - -* Beware of turnips. Turnips are disgusting. - -## _To do_ list - -* Implement some traits (`Clone`, `Drop`, `Debug` are good candidates). -* Provide built-in support for user-space array and hash BPF maps. -* Improve safety of JIT-compiled programs with runtime memory checks. -* Add helpers (some of those supported in the kernel, such as checksum update, - could be helpful). -* Improve verifier. Could we find a way to directly support programs compiled - with clang? -* Maybe one day, tail calls? -* JIT-compilers for other architectures? -* … - -## License - -Following the effort of the Rust language project itself in order to ease -integration with other projects, the rbpf crate is distributed under the terms -of both the MIT license and the Apache License (Version 2.0). - -See -[LICENSE-APACHE](https://github.com/qmonnet/rbpf/blob/main/LICENSE-APACHE) -and [LICENSE-MIT](https://github.com/qmonnet/rbpf/blob/main/LICENSE-MIT) for -details. - -## Version -[The last commit](https://github.com/qmonnet/rbpf/commit/fe7021b07b08a43b836743a77796d07ce1f4902e) - - -## Inspired by - -* [uBPF](https://github.com/iovisor/ubpf), a C user-space implementation of an - eBPF virtual machine, with a JIT-compiler and disassembler (and also - including the assembler from the human-readable form of the instructions, - such as in `mov r0, 0x1337`), by Rich Lane for Big Switch Networks (2015) - -* [_Building a simple JIT in - Rust_](https://www.sophiajt.com/building-a-simple-jit-in-rust), - by Sophia Turner (2015) - -* [bpfjit](https://github.com/polachok/bpfjit) (also [on - crates.io](https://crates.io/crates/bpfjit)), a Rust crate exporting the cBPF - JIT compiler from FreeBSD 10 tree to Rust, by Alexander Polakov (2016) - -## Other resources - -* Cilium project documentation about BPF: [_BPF and XDP Reference - Guide_](http://docs.cilium.io/en/latest/bpf/) - -* [Kernel documentation about BPF](https://docs.kernel.org/bpf/) - -* [_Dive into BPF: a list of reading - material_](https://qmonnet.github.io/whirl-offload/2016/09/01/dive-into-bpf), - a blog article listing documentation for BPF and related technologies (2016) - -* [The Rust programming language](https://www.rust-lang.org) diff --git a/kernel/crates/rbpf/clippy.toml b/kernel/crates/rbpf/clippy.toml deleted file mode 100644 index 1d4a2968..00000000 --- a/kernel/crates/rbpf/clippy.toml +++ /dev/null @@ -1 +0,0 @@ -doc-valid-idents = ["eBPF", "uBPF"] diff --git a/kernel/crates/rbpf/examples/disassemble.rs b/kernel/crates/rbpf/examples/disassemble.rs deleted file mode 100644 index 4dbcc277..00000000 --- a/kernel/crates/rbpf/examples/disassemble.rs +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: (Apache-2.0 OR MIT) -// Copyright 2017 6WIND S.A. - -extern crate rbpf; -use rbpf::disassembler; - -// Simply disassemble a program into human-readable instructions. -fn main() { - let prog = &[ - 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x12, 0x50, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x79, 0x11, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbf, 0x13, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x07, 0x03, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x2d, 0x23, 0x12, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x69, 0x12, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x02, 0x10, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x71, 0x12, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x02, 0x0e, - 0x00, 0x06, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbf, - 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x02, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, - 0x15, 0x02, 0x08, 0x00, 0x99, 0x99, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00, 0x00, 0x00, 0xff, - 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x21, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xb7, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x18, 0x02, 0x00, 0x00, 0x00, - 0x00, 0x99, 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x21, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, - ]; - disassembler::disassemble(prog); -} diff --git a/kernel/crates/rbpf/examples/helper.rs b/kernel/crates/rbpf/examples/helper.rs deleted file mode 100644 index ace3dfef..00000000 --- a/kernel/crates/rbpf/examples/helper.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - rbpf::helpers::show_helper(); -} diff --git a/kernel/crates/rbpf/examples/load_elf.rs b/kernel/crates/rbpf/examples/load_elf.rs deleted file mode 100644 index 9d794140..00000000 --- a/kernel/crates/rbpf/examples/load_elf.rs +++ /dev/null @@ -1,115 +0,0 @@ -// SPDX-License-Identifier: (Apache-2.0 OR MIT) -// Copyright 2016 6WIND S.A. - -#![allow(clippy::unreadable_literal)] - -extern crate elf; -use std::path::PathBuf; - -extern crate rbpf; -use rbpf::helpers; - -// The following example uses an ELF file that has been compiled from the C program available in -// `load_elf__block_a_port.c` in the same directory. -// -// It was compiled with the following command: -// -// ```bash -// clang -O2 -emit-llvm -c load_elf__block_a_port.c -o - | \ -// llc -march=bpf -filetype=obj -o load_elf__block_a_port.o -// ``` -// -// Once compiled, this program can be injected into Linux kernel, with tc for instance. Sadly, we -// need to bring some modifications to the generated bytecode in order to run it: the three -// instructions with opcode 0x61 load data from a packet area as 4-byte words, where we need to -// load it as 8-bytes double words (0x79). The kernel does the same kind of translation before -// running the program, but rbpf does not implement this. -// -// In addition, the offset at which the pointer to the packet data is stored must be changed: since -// we use 8 bytes instead of 4 for the start and end addresses of the data packet, we cannot use -// the offsets produced by clang (0x4c and 0x50), the addresses would overlap. Instead we can use, -// for example, 0x40 and 0x50. -// -// These change were applied with the following script: -// -// ```bash -// xxd load_elf__block_a_port.o | sed ' -// s/6112 5000 0000 0000/7912 5000 0000 0000/ ; -// s/6111 4c00 0000 0000/7911 4000 0000 0000/ ; -// s/6111 2200 0000 0000/7911 2200 0000 0000/' | xxd -r > load_elf__block_a_port.tmp - -// mv load_elf__block_a_port.tmp load_elf__block_a_port.o -// ``` -// -// The eBPF program was placed into the `.classifier` ELF section (see C code above), which means -// that you can retrieve the raw bytecode with `readelf -x .classifier load_elf__block_a_port.o` or -// with `objdump -s -j .classifier load_elf__block_a_port.o`. -// -// Once the bytecode has been edited, we can load the bytecode directly from the ELF object file. - -fn main() { - let filename = "examples/load_elf__block_a_port.elf"; - - let path = PathBuf::from(filename); - let file = match elf::File::open_path(path) { - Ok(f) => f, - Err(e) => panic!("Error: {:?}", e), - }; - - let text_scn = match file.get_section(".classifier") { - Some(s) => s, - None => panic!("Failed to look up .classifier section"), - }; - - let prog = &text_scn.data; - - let packet1 = &mut [ - 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x08, - 0x00, // ethertype - 0x45, 0x00, 0x00, 0x3b, // start ip_hdr - 0xa6, 0xab, 0x40, 0x00, 0x40, 0x06, 0x96, 0x0f, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00, 0x00, - 0x01, - // Program matches the next two bytes: 0x9999 returns 0xffffffff, else return 0. - 0x99, 0x99, 0xc6, 0xcc, // start tcp_hdr - 0xd1, 0xe5, 0xc4, 0x9d, 0xd4, 0x30, 0xb5, 0xd2, 0x80, 0x18, 0x01, 0x56, 0xfe, 0x2f, 0x00, - 0x00, 0x01, 0x01, 0x08, 0x0a, // start data - 0x00, 0x23, 0x75, 0x89, 0x00, 0x23, 0x63, 0x2d, 0x71, 0x64, 0x66, 0x73, 0x64, 0x66, 0x0au8, - ]; - - let packet2 = &mut [ - 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x08, - 0x00, // ethertype - 0x45, 0x00, 0x00, 0x3b, // start ip_hdr - 0xa6, 0xab, 0x40, 0x00, 0x40, 0x06, 0x96, 0x0f, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00, 0x00, - 0x01, - // Program matches the next two bytes: 0x9999 returns 0xffffffff, else return 0. - 0x98, 0x76, 0xc6, 0xcc, // start tcp_hdr - 0xd1, 0xe5, 0xc4, 0x9d, 0xd4, 0x30, 0xb5, 0xd2, 0x80, 0x18, 0x01, 0x56, 0xfe, 0x2f, 0x00, - 0x00, 0x01, 0x01, 0x08, 0x0a, // start data - 0x00, 0x23, 0x75, 0x89, 0x00, 0x23, 0x63, 0x2d, 0x71, 0x64, 0x66, 0x73, 0x64, 0x66, 0x0au8, - ]; - - let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap(); - vm.register_helper(helpers::BPF_TRACE_PRINTK_IDX, helpers::bpf_trace_printf) - .unwrap(); - - let res = vm.execute_program(packet1).unwrap(); - println!("Packet #1, program returned: {res:?} ({res:#x})"); - assert_eq!(res, 0xffffffff); - - #[cfg(not(windows))] - { - vm.jit_compile().unwrap(); - - let res = unsafe { vm.execute_program_jit(packet2).unwrap() }; - println!("Packet #2, program returned: {res:?} ({res:#x})"); - assert_eq!(res, 0); - } - - #[cfg(windows)] - { - let res = vm.execute_program(packet2).unwrap(); - println!("Packet #2, program returned: {:?} ({:#x})", res, res); - assert_eq!(res, 0); - } -} diff --git a/kernel/crates/rbpf/examples/load_elf__block_a_port.c b/kernel/crates/rbpf/examples/load_elf__block_a_port.c deleted file mode 100644 index 0a51117e..00000000 --- a/kernel/crates/rbpf/examples/load_elf__block_a_port.c +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: (APACHE-2.0 OR MIT) -// Copyright 2016 6WIND S.A. - -// Block TCP packets on source or destination port 0x9999. - -#include -#include -#include -#include - -#define ETH_ALEN 6 -#define ETH_P_IP 0x0008 /* htons(0x0800) */ -#define TCP_HDR_LEN 20 - -#define BLOCKED_TCP_PORT 0x9999 - -struct eth_hdr { - unsigned char h_dest[ETH_ALEN]; - unsigned char h_source[ETH_ALEN]; - unsigned short h_proto; -}; - -#define SEC(NAME) __attribute__((section(NAME), used)) -SEC(".classifier") -int handle_ingress(struct __sk_buff *skb) -{ - void *data = (void *)(long)skb->data; - void *data_end = (void *)(long)skb->data_end; - struct eth_hdr *eth = data; - struct iphdr *iph = data + sizeof(*eth); - struct tcphdr *tcp = data + sizeof(*eth) + sizeof(*iph); - - /* single length check */ - if (data + sizeof(*eth) + sizeof(*iph) + sizeof(*tcp) > data_end) - return 0; - if (eth->h_proto != ETH_P_IP) - return 0; - if (iph->protocol != IPPROTO_TCP) - return 0; - if (tcp->source == BLOCKED_TCP_PORT || tcp->dest == BLOCKED_TCP_PORT) - return -1; - return 0; -} diff --git a/kernel/crates/rbpf/examples/rbpf_plugin.rs b/kernel/crates/rbpf/examples/rbpf_plugin.rs deleted file mode 100644 index 7a7ace6e..00000000 --- a/kernel/crates/rbpf/examples/rbpf_plugin.rs +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright Microsoft Corporation -// SPDX-License-Identifier: (Apache-2.0 OR MIT) - -// Path: examples/rbpf_plugin.rs -use std::io::Read; - -// Helper function used by https://github.com/Alan-Jowett/bpf_conformance/blob/main/tests/call_unwind_fail.data -fn _unwind(a: u64, _b: u64, _c: u64, _d: u64, _e: u64) -> u64 { - a -} - -// This is a plugin for the bpf_conformance test suite (https://github.com/Alan-Jowett/bpf_conformance) -// It accepts a single argument, the memory contents to pass to the VM. -// It reads the program from stdin. -fn main() { - let mut args: Vec = std::env::args().collect(); - #[allow(unused_mut)] // In no_std the jit variable isn't mutated. - let mut jit: bool = false; - let mut cranelift: bool = false; - let mut program_text = String::new(); - let mut memory_text = String::new(); - - args.remove(0); - - // Memory is always the first argument. - if !args.is_empty() { - memory_text.clone_from(&args[0]); - // Strip whitespace - memory_text.retain(|c| !c.is_whitespace()); - args.remove(0); - } - - // Process the rest of the arguments. - while !args.is_empty() { - match args[0].as_str() { - "--help" => { - println!("Usage: rbpf_plugin [memory] < program"); - return; - } - "--jit" => { - #[cfg(any(windows, not(feature = "std")))] - { - println!("JIT not supported"); - return; - } - #[cfg(all(not(windows), feature = "std"))] - { - jit = true; - } - } - "--cranelift" => { - cranelift = true; - - #[cfg(not(feature = "cranelift"))] - { - let _ = cranelift; - println!("Cranelift is not enabled"); - return; - } - } - "--program" => { - if args.len() < 2 { - println!("Missing argument to --program"); - return; - } - args.remove(0); - if !args.is_empty() { - program_text.clone_from(&args[0]); - args.remove(0); - } - } - _ => panic!("Unknown argument {}", args[0]), - } - args.remove(0); - } - - if program_text.is_empty() { - // Read program text from stdin - std::io::stdin().read_to_string(&mut program_text).unwrap(); - } - - // Strip whitespace - program_text.retain(|c| !c.is_whitespace()); - - // Convert program from hex to bytecode - let bytecode = hex::decode(program_text).unwrap(); - - // Convert memory from hex to bytes - let mut memory: Vec = hex::decode(memory_text).unwrap(); - - // Create rbpf vm - let mut vm = rbpf::EbpfVmRaw::new(Some(&bytecode)).unwrap(); - - // Register the helper function used by call_unwind_fail.data test. - vm.register_helper(5, _unwind).unwrap(); - - let result: u64; - if jit { - #[cfg(any(windows, not(feature = "std")))] - { - println!("JIT not supported"); - return; - } - #[cfg(all(not(windows), feature = "std"))] - { - unsafe { - vm.jit_compile().unwrap(); - result = vm.execute_program_jit(&mut memory).unwrap(); - } - } - } else if cranelift { - #[cfg(not(feature = "cranelift"))] - { - println!("Cranelift is not enabled"); - return; - } - #[cfg(feature = "cranelift")] - { - vm.cranelift_compile().unwrap(); - result = vm.execute_program_cranelift(&mut memory).unwrap(); - } - } else { - result = vm.execute_program(&mut memory).unwrap(); - } - println!("{result:x}"); -} diff --git a/kernel/crates/rbpf/examples/to_json.rs b/kernel/crates/rbpf/examples/to_json.rs deleted file mode 100644 index 6b86047d..00000000 --- a/kernel/crates/rbpf/examples/to_json.rs +++ /dev/null @@ -1,74 +0,0 @@ -// SPDX-License-Identifier: (Apache-2.0 OR MIT) -// Copyright 2017 6WIND S.A. - -#[macro_use] -extern crate json; - -extern crate elf; -use std::path::PathBuf; - -extern crate rbpf; -use rbpf::disassembler; - -// Turn a program into a JSON string. -// -// Relies on `json` crate. -// -// You may copy this function and adapt it according to your needs. For instance, you may want to: -// -// * Remove the "desc" (description) attributes from the output. -// * Print integers as integers, and not as strings containing their hexadecimal representation -// (just replace the relevant `format!()` calls by the commented values. -fn to_json(prog: &[u8]) -> String { - // This call returns a high-level representation of the instructions, with the two parts of - // `LD_DW_IMM` instructions merged, and name and descriptions of the instructions. - // If you prefer to use a lower-level representation, use `ebpf::to_insn_vec()` function - // instead. - let insns = disassembler::to_insn_vec(prog); - let mut json_insns = vec![]; - for insn in insns { - json_insns.push(object!( - "opc" => format!("{:#x}", insn.opc), // => insn.opc, - "dst" => format!("{:#x}", insn.dst), // => insn.dst, - "src" => format!("{:#x}", insn.src), // => insn.src, - "off" => format!("{:#x}", insn.off), // => insn.off, - // Warning: for imm we use a i64 instead of a i32 (to have correct values for - // `lddw` operation. If we print a number in the JSON this is not a problem, the - // internal i64 has the same value with extended sign on 32 most significant bytes. - // If we print the hexadecimal value as a string however, we want to cast as a i32 - // to prevent all other instructions to print spurious `ffffffff` prefix if the - // number is negative. When values takes more than 32 bits with `lddw`, the cast - // has no effect and the complete value is printed anyway. - "imm" => format!("{:#x}", insn.imm as i32), // => insn.imm, - "desc" => insn.desc - )); - } - json::stringify_pretty( - object!( - "size" => json_insns.len(), - "insns" => json_insns - ), - 4, - ) -} - -// Load a program from an object file, and prints it to standard output as a JSON string. -fn main() { - // Let's reuse this file from `load_elf/example`. - let filename = "examples/load_elf__block_a_port.elf"; - - let path = PathBuf::from(filename); - let file = match elf::File::open_path(path) { - Ok(f) => f, - Err(e) => panic!("Error: {:?}", e), - }; - - let text_scn = match file.get_section(".classifier") { - Some(s) => s, - None => panic!("Failed to look up .classifier section"), - }; - - let prog = &text_scn.data; - - println!("{}", to_json(prog)); -} diff --git a/kernel/crates/rbpf/examples/uptime.rs b/kernel/crates/rbpf/examples/uptime.rs deleted file mode 100644 index 49b1642a..00000000 --- a/kernel/crates/rbpf/examples/uptime.rs +++ /dev/null @@ -1,78 +0,0 @@ -// SPDX-License-Identifier: (Apache-2.0 OR MIT) -// Copyright 2017 6WIND S.A. - -extern crate rbpf; -use rbpf::helpers; - -// The main objectives of this example is to show: -// -// * the use of EbpfVmNoData function, -// * and the use of a helper. -// -// The two eBPF programs are independent and are not related to one another. -fn main() { - let prog1 = &[ - 0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov32 r0, 0 - 0xb4, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, // mov32 r1, 2 - 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // add32 r0, 1 - 0x0c, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // add32 r0, r1 - 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exit and return r0 - ]; - - // We use helper `bpf_time_getns()`, which is similar to helper `bpf_ktime_getns()` from Linux - // kernel. Hence rbpf::helpers module provides the index of this in-kernel helper as a - // constant, so that we can remain compatible with programs for the kernel. Here we also cast - // it to a u8 so as to use it directly in program instructions. - let hkey = helpers::BPF_KTIME_GETNS_IDX as u8; - let prog2 = &[ - 0xb7, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov64 r1, 0 - 0xb7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov64 r1, 0 - 0xb7, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov64 r1, 0 - 0xb7, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov64 r1, 0 - 0xb7, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov64 r1, 0 - 0x85, 0x00, 0x00, 0x00, hkey, 0x00, 0x00, 0x00, // call helper - 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exit and return r0 - ]; - - // Create a VM: this one takes no data. Load prog1 in it. - let mut vm = rbpf::EbpfVmNoData::new(Some(prog1)).unwrap(); - // Execute prog1. - assert_eq!(vm.execute_program().unwrap(), 0x3); - - // As struct EbpfVmNoData does not takes any memory area, its return value is mostly - // deterministic. So we know prog1 will always return 3. There is an exception: when it uses - // helpers, the latter may have non-deterministic values, and all calls may not return the same - // value. - // - // In the following example we use a helper to get the elapsed time since boot time: we - // reimplement uptime in eBPF, in Rust. Because why not. - - vm.set_program(prog2).unwrap(); - vm.register_helper(helpers::BPF_KTIME_GETNS_IDX, helpers::bpf_time_getns) - .unwrap(); - - let time; - - #[cfg(all(not(windows), feature = "std"))] - { - vm.jit_compile().unwrap(); - - time = unsafe { vm.execute_program_jit().unwrap() }; - } - - #[cfg(any(windows, not(feature = "std")))] - { - time = vm.execute_program().unwrap(); - } - - let days = time / 10u64.pow(9) / 60 / 60 / 24; - let hours = (time / 10u64.pow(9) / 60 / 60) % 24; - let minutes = (time / 10u64.pow(9) / 60) % 60; - let seconds = (time / 10u64.pow(9)) % 60; - let nanosec = time % 10u64.pow(9); - - println!( - "Uptime: {:#x} ns == {} days {:02}:{:02}:{:02}, {} ns", - time, days, hours, minutes, seconds, nanosec - ); -} diff --git a/kernel/crates/rbpf/mk/appveyor.bat b/kernel/crates/rbpf/mk/appveyor.bat deleted file mode 100644 index 06f61d39..00000000 --- a/kernel/crates/rbpf/mk/appveyor.bat +++ /dev/null @@ -1,72 +0,0 @@ -echo on -SetLocal EnableDelayedExpansion - -REM This is the recommended way to choose the toolchain version, according to -REM Appveyor's documentation. -SET PATH=C:\Program Files (x86)\MSBuild\%TOOLCHAIN_VERSION%\Bin;%PATH% - -set VCVARSALL="C:\Program Files (x86)\Microsoft Visual Studio %TOOLCHAIN_VERSION%\VC\vcvarsall.bat" - -if [%Platform%] NEQ [x64] goto win32 -set TARGET_ARCH=x86_64 -set TARGET_PROGRAM_FILES=%ProgramFiles% -call %VCVARSALL% amd64 -if %ERRORLEVEL% NEQ 0 exit 1 -goto download - -:win32 -echo on -if [%Platform%] NEQ [Win32] exit 1 -set TARGET_ARCH=i686 -set TARGET_PROGRAM_FILES=%ProgramFiles(x86)% -call %VCVARSALL% amd64_x86 -if %ERRORLEVEL% NEQ 0 exit 1 -goto download - -:download -REM vcvarsall turns echo off -echo on - -mkdir windows_build_tools -mkdir windows_build_tools\ -echo Downloading Yasm... -powershell -Command "(New-Object Net.WebClient).DownloadFile('http://www.tortall.net/projects/yasm/releases/yasm-1.3.0-win64.exe', 'windows_build_tools\yasm.exe')" -if %ERRORLEVEL% NEQ 0 ( - echo ...downloading Yasm failed. - exit 1 -) - -set RUST_URL=https://static.rust-lang.org/dist/rust-%RUST%-%TARGET_ARCH%-pc-windows-msvc.msi -echo Downloading %RUST_URL%... -mkdir build -powershell -Command "(New-Object Net.WebClient).DownloadFile('%RUST_URL%', 'build\rust-%RUST%-%TARGET_ARCH%-pc-windows-msvc.msi')" -if %ERRORLEVEL% NEQ 0 ( - echo ...downloading Rust failed. - exit 1 -) - -start /wait msiexec /i build\rust-%RUST%-%TARGET_ARCH%-pc-windows-msvc.msi INSTALLDIR="%TARGET_PROGRAM_FILES%\Rust %RUST%" /quiet /qn /norestart -if %ERRORLEVEL% NEQ 0 exit 1 - -set PATH="%TARGET_PROGRAM_FILES%\Rust %RUST%\bin";%cd%\windows_build_tools;%PATH% - -if [%Configuration%] == [Release] set CARGO_MODE=--release - -set - -link /? -cl /? -rustc --version -cargo --version - -cargo test --all-features -vv %CARGO_MODE% -if %ERRORLEVEL% NEQ 0 exit 1 - -REM Verify that `cargo build`, independent from `cargo test`, works; i.e. -REM verify that non-test builds aren't trying to use test-only features. -cargo build -vv %CARGO_MODE% -if %ERRORLEVEL% NEQ 0 exit 1 - -REM Verify that we can build with all features -cargo build --all-features -vv %CARGO_MODE% -if %ERRORLEVEL% NEQ 0 exit 1 diff --git a/kernel/crates/rbpf/rustfmt.toml b/kernel/crates/rbpf/rustfmt.toml deleted file mode 100644 index 8a0a3841..00000000 --- a/kernel/crates/rbpf/rustfmt.toml +++ /dev/null @@ -1,3 +0,0 @@ -group_imports="StdExternalCrate" -reorder_imports=true -imports_granularity="Crate" \ No newline at end of file diff --git a/kernel/crates/rbpf/src/asm_parser.rs b/kernel/crates/rbpf/src/asm_parser.rs deleted file mode 100644 index 41969218..00000000 --- a/kernel/crates/rbpf/src/asm_parser.rs +++ /dev/null @@ -1,642 +0,0 @@ -// SPDX-License-Identifier: (Apache-2.0 OR MIT) -// Copyright 2017 Rich Lane - -// Rust-doc comments were left in the module, but it is no longer publicly exposed from the root -// file of the crate. Do not expect to find those comments in the documentation of the crate. - -//! This module parses eBPF assembly language source code. - -use alloc::{ - string::{String, ToString}, - vec::Vec, -}; - -#[cfg(feature = "std")] -use combine::EasyParser; -use combine::{ - attempt, between, eof, many, many1, one_of, optional, - parser::char::{alpha_num, char, digit, hex_digit, spaces, string}, - sep_by, - stream::position::{self}, - ParseError, Parser, Stream, -}; - -/// Operand of an instruction. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum Operand { - /// Register number. - Register(i64), - /// Jump offset or immediate. - Integer(i64), - /// Register number and offset. - Memory(i64, i64), - /// Used for pattern matching. - Nil, -} - -/// Parsed instruction. -#[derive(Debug, PartialEq, Eq)] -pub struct Instruction { - /// Instruction name. - pub name: String, - /// Operands. - pub operands: Vec, -} - -fn ident() -> impl Parser -where - I: Stream, - I::Error: ParseError, -{ - many1(alpha_num()) -} - -fn integer() -> impl Parser -where - I: Stream, - I::Error: ParseError, -{ - let sign = optional(one_of("-+".chars())).map(|x| match x { - Some('-') => -1, - _ => 1, - }); - let hex = string("0x") - .with(many1(hex_digit())) - .map(|x: String| u64::from_str_radix(&x, 16).unwrap() as i64); - let dec = many1(digit()).map(|x: String| x.parse::().unwrap()); - (sign, attempt(hex).or(dec)).map(|(s, x)| s * x) -} - -fn register() -> impl Parser -where - I: Stream, - I::Error: ParseError, -{ - char('r') - .with(many1(digit())) - .map(|x: String| x.parse::().unwrap()) -} - -fn operand() -> impl Parser -where - I: Stream, - I::Error: ParseError, -{ - let register_operand = register().map(Operand::Register); - let immediate = integer().map(Operand::Integer); - let memory = between(char('['), char(']'), (register(), optional(integer()))) - .map(|t| Operand::Memory(t.0, t.1.unwrap_or(0))); - register_operand.or(immediate).or(memory) -} - -fn instruction() -> impl Parser -where - I: Stream, - I::Error: ParseError, -{ - let operands = sep_by(operand(), char(',').skip(spaces())); - (ident().skip(spaces()), operands, spaces()).map(|t| Instruction { - name: t.0, - operands: t.1, - }) -} - -/// Parse a string into a list of instructions. -/// -/// The instructions are not validated and may have invalid names and operand types. -pub fn parse(input: &str) -> Result, String> { - let mut with = spaces().with(many(instruction()).skip(eof())); - - #[cfg(feature = "std")] - { - match with.easy_parse(position::Stream::new(input)) { - Ok((insts, _)) => Ok(insts), - Err(err) => Err(err.to_string()), - } - } - #[cfg(not(feature = "std"))] - { - match with.parse(position::Stream::new(input)) { - Ok((insts, _)) => Ok(insts), - Err(err) => Err(err.to_string()), - } - } -} - -#[cfg(test)] -mod tests { - use alloc::{string::ToString, vec}; - - use combine::Parser; - - use super::{ident, instruction, integer, operand, parse, register, Instruction, Operand}; - - // Unit tests for the different kinds of parsers. - - #[test] - fn test_ident() { - assert_eq!(ident().parse("nop"), Ok(("nop".to_string(), ""))); - assert_eq!(ident().parse("add32"), Ok(("add32".to_string(), ""))); - assert_eq!(ident().parse("add32*"), Ok(("add32".to_string(), "*"))); - } - - #[test] - fn test_integer() { - assert_eq!(integer().parse("0"), Ok((0, ""))); - assert_eq!(integer().parse("42"), Ok((42, ""))); - assert_eq!(integer().parse("+42"), Ok((42, ""))); - assert_eq!(integer().parse("-42"), Ok((-42, ""))); - assert_eq!(integer().parse("0x0"), Ok((0, ""))); - assert_eq!( - integer().parse("0x123456789abcdef0"), - Ok((0x123456789abcdef0, "")) - ); - assert_eq!(integer().parse("-0x1f"), Ok((-31, ""))); - } - - #[test] - fn test_register() { - assert_eq!(register().parse("r0"), Ok((0, ""))); - assert_eq!(register().parse("r15"), Ok((15, ""))); - } - - #[test] - fn test_operand() { - assert_eq!(operand().parse("r0"), Ok((Operand::Register(0), ""))); - assert_eq!(operand().parse("r15"), Ok((Operand::Register(15), ""))); - assert_eq!(operand().parse("0"), Ok((Operand::Integer(0), ""))); - assert_eq!(operand().parse("42"), Ok((Operand::Integer(42), ""))); - assert_eq!(operand().parse("[r1]"), Ok((Operand::Memory(1, 0), ""))); - assert_eq!(operand().parse("[r3+5]"), Ok((Operand::Memory(3, 5), ""))); - assert_eq!( - operand().parse("[r3+0x1f]"), - Ok((Operand::Memory(3, 31), "")) - ); - assert_eq!( - operand().parse("[r3-0x1f]"), - Ok((Operand::Memory(3, -31), "")) - ); - } - - #[test] - fn test_instruction() { - assert_eq!( - instruction().parse("exit"), - Ok(( - Instruction { - name: "exit".to_string(), - operands: vec![], - }, - "" - )) - ); - - assert_eq!( - instruction().parse("call 2"), - Ok(( - Instruction { - name: "call".to_string(), - operands: vec![Operand::Integer(2)], - }, - "" - )) - ); - - assert_eq!( - instruction().parse("addi r1, 2"), - Ok(( - Instruction { - name: "addi".to_string(), - operands: vec![Operand::Register(1), Operand::Integer(2)], - }, - "" - )) - ); - - assert_eq!( - instruction().parse("ldxb r2, [r1+12]"), - Ok(( - Instruction { - name: "ldxb".to_string(), - operands: vec![Operand::Register(2), Operand::Memory(1, 12)], - }, - "" - )) - ); - - assert_eq!( - instruction().parse("lsh r3, 0x8"), - Ok(( - Instruction { - name: "lsh".to_string(), - operands: vec![Operand::Register(3), Operand::Integer(8)], - }, - "" - )) - ); - - assert_eq!( - instruction().parse("jne r3, 0x8, +37"), - Ok(( - Instruction { - name: "jne".to_string(), - operands: vec![ - Operand::Register(3), - Operand::Integer(8), - Operand::Integer(37) - ], - }, - "" - )) - ); - - // Whitespace between operands is optional. - assert_eq!( - instruction().parse("jne r3,0x8,+37"), - Ok(( - Instruction { - name: "jne".to_string(), - operands: vec![ - Operand::Register(3), - Operand::Integer(8), - Operand::Integer(37) - ], - }, - "" - )) - ); - } - - // Other unit tests: try to parse various set of instructions. - - #[test] - fn test_empty() { - assert_eq!(parse(""), Ok(vec![])); - } - - #[test] - fn test_exit() { - // No operands. - assert_eq!( - parse("exit"), - Ok(vec![Instruction { - name: "exit".to_string(), - operands: vec![], - }]) - ); - } - - #[test] - fn test_lsh() { - // Register and immediate operands. - assert_eq!( - parse("lsh r3, 0x20"), - Ok(vec![Instruction { - name: "lsh".to_string(), - operands: vec![Operand::Register(3), Operand::Integer(0x20)], - }]) - ); - } - - #[test] - fn test_ja() { - // Jump offset operand. - assert_eq!( - parse("ja +1"), - Ok(vec![Instruction { - name: "ja".to_string(), - operands: vec![Operand::Integer(1)], - }]) - ); - } - - #[test] - fn test_ldxh() { - // Register and memory operands. - assert_eq!( - parse("ldxh r4, [r1+12]"), - Ok(vec![Instruction { - name: "ldxh".to_string(), - operands: vec![Operand::Register(4), Operand::Memory(1, 12)], - }]) - ); - } - - #[test] - fn test_tcp_sack() { - // Sample program from ubpf. - // We could technically indent the instructions since the parser support white spaces at - // the beginning, but there is another test for that. - let src = "\ -ldxb r2, [r1+12] -ldxb r3, [r1+13] -lsh r3, 0x8 -or r3, r2 -mov r0, 0x0 -jne r3, 0x8, +37 -ldxb r2, [r1+23] -jne r2, 0x6, +35 -ldxb r2, [r1+14] -add r1, 0xe -and r2, 0xf -lsh r2, 0x2 -add r1, r2 -mov r0, 0x0 -ldxh r4, [r1+12] -add r1, 0x14 -rsh r4, 0x2 -and r4, 0x3c -mov r2, r4 -add r2, 0xffffffec -mov r5, 0x15 -mov r3, 0x0 -jgt r5, r4, +20 -mov r5, r3 -lsh r5, 0x20 -arsh r5, 0x20 -mov r4, r1 -add r4, r5 -ldxb r5, [r4] -jeq r5, 0x1, +4 -jeq r5, 0x0, +12 -mov r6, r3 -jeq r5, 0x5, +9 -ja +2 -add r3, 0x1 -mov r6, r3 -ldxb r3, [r4+1] -add r3, r6 -lsh r3, 0x20 -arsh r3, 0x20 -jsgt r2, r3, -18 -ja +1 -mov r0, 0x1 -exit -"; - - assert_eq!( - parse(src), - Ok(vec![ - Instruction { - name: "ldxb".to_string(), - operands: vec![Operand::Register(2), Operand::Memory(1, 12)], - }, - Instruction { - name: "ldxb".to_string(), - operands: vec![Operand::Register(3), Operand::Memory(1, 13)], - }, - Instruction { - name: "lsh".to_string(), - operands: vec![Operand::Register(3), Operand::Integer(8)], - }, - Instruction { - name: "or".to_string(), - operands: vec![Operand::Register(3), Operand::Register(2)], - }, - Instruction { - name: "mov".to_string(), - operands: vec![Operand::Register(0), Operand::Integer(0)], - }, - Instruction { - name: "jne".to_string(), - operands: vec![ - Operand::Register(3), - Operand::Integer(8), - Operand::Integer(37) - ], - }, - Instruction { - name: "ldxb".to_string(), - operands: vec![Operand::Register(2), Operand::Memory(1, 23)], - }, - Instruction { - name: "jne".to_string(), - operands: vec![ - Operand::Register(2), - Operand::Integer(6), - Operand::Integer(35) - ], - }, - Instruction { - name: "ldxb".to_string(), - operands: vec![Operand::Register(2), Operand::Memory(1, 14)], - }, - Instruction { - name: "add".to_string(), - operands: vec![Operand::Register(1), Operand::Integer(14)], - }, - Instruction { - name: "and".to_string(), - operands: vec![Operand::Register(2), Operand::Integer(15)], - }, - Instruction { - name: "lsh".to_string(), - operands: vec![Operand::Register(2), Operand::Integer(2)], - }, - Instruction { - name: "add".to_string(), - operands: vec![Operand::Register(1), Operand::Register(2)], - }, - Instruction { - name: "mov".to_string(), - operands: vec![Operand::Register(0), Operand::Integer(0)], - }, - Instruction { - name: "ldxh".to_string(), - operands: vec![Operand::Register(4), Operand::Memory(1, 12)], - }, - Instruction { - name: "add".to_string(), - operands: vec![Operand::Register(1), Operand::Integer(20)], - }, - Instruction { - name: "rsh".to_string(), - operands: vec![Operand::Register(4), Operand::Integer(2)], - }, - Instruction { - name: "and".to_string(), - operands: vec![Operand::Register(4), Operand::Integer(60)], - }, - Instruction { - name: "mov".to_string(), - operands: vec![Operand::Register(2), Operand::Register(4)], - }, - Instruction { - name: "add".to_string(), - operands: vec![Operand::Register(2), Operand::Integer(4294967276)], - }, - Instruction { - name: "mov".to_string(), - operands: vec![Operand::Register(5), Operand::Integer(21)], - }, - Instruction { - name: "mov".to_string(), - operands: vec![Operand::Register(3), Operand::Integer(0)], - }, - Instruction { - name: "jgt".to_string(), - operands: vec![ - Operand::Register(5), - Operand::Register(4), - Operand::Integer(20) - ], - }, - Instruction { - name: "mov".to_string(), - operands: vec![Operand::Register(5), Operand::Register(3)], - }, - Instruction { - name: "lsh".to_string(), - operands: vec![Operand::Register(5), Operand::Integer(32)], - }, - Instruction { - name: "arsh".to_string(), - operands: vec![Operand::Register(5), Operand::Integer(32)], - }, - Instruction { - name: "mov".to_string(), - operands: vec![Operand::Register(4), Operand::Register(1)], - }, - Instruction { - name: "add".to_string(), - operands: vec![Operand::Register(4), Operand::Register(5)], - }, - Instruction { - name: "ldxb".to_string(), - operands: vec![Operand::Register(5), Operand::Memory(4, 0)], - }, - Instruction { - name: "jeq".to_string(), - operands: vec![ - Operand::Register(5), - Operand::Integer(1), - Operand::Integer(4) - ], - }, - Instruction { - name: "jeq".to_string(), - operands: vec![ - Operand::Register(5), - Operand::Integer(0), - Operand::Integer(12) - ], - }, - Instruction { - name: "mov".to_string(), - operands: vec![Operand::Register(6), Operand::Register(3)], - }, - Instruction { - name: "jeq".to_string(), - operands: vec![ - Operand::Register(5), - Operand::Integer(5), - Operand::Integer(9) - ], - }, - Instruction { - name: "ja".to_string(), - operands: vec![Operand::Integer(2)], - }, - Instruction { - name: "add".to_string(), - operands: vec![Operand::Register(3), Operand::Integer(1)], - }, - Instruction { - name: "mov".to_string(), - operands: vec![Operand::Register(6), Operand::Register(3)], - }, - Instruction { - name: "ldxb".to_string(), - operands: vec![Operand::Register(3), Operand::Memory(4, 1)], - }, - Instruction { - name: "add".to_string(), - operands: vec![Operand::Register(3), Operand::Register(6)], - }, - Instruction { - name: "lsh".to_string(), - operands: vec![Operand::Register(3), Operand::Integer(32)], - }, - Instruction { - name: "arsh".to_string(), - operands: vec![Operand::Register(3), Operand::Integer(32)], - }, - Instruction { - name: "jsgt".to_string(), - operands: vec![ - Operand::Register(2), - Operand::Register(3), - Operand::Integer(-18) - ], - }, - Instruction { - name: "ja".to_string(), - operands: vec![Operand::Integer(1)], - }, - Instruction { - name: "mov".to_string(), - operands: vec![Operand::Register(0), Operand::Integer(1)], - }, - Instruction { - name: "exit".to_string(), - operands: vec![], - } - ]) - ); - } - - /// When running without `std` the `EasyParser` provided by `combine` - /// cannot be used. Because of this we need to use the `Parser` and the - /// error messages are different. - #[test] - fn test_error_eof() { - let expected_error; - #[cfg(feature = "std")] - { - expected_error = Err( - "Parse error at line: 1, column: 6\nUnexpected end of input\nExpected digit\n" - .to_string(), - ); - } - #[cfg(not(feature = "std"))] - { - expected_error = Err("unexpected parse".to_string()); - } - // Unexpected end of input in a register name. - assert_eq!(parse("lsh r"), expected_error); - } - - /// When running without `std` the `EasyParser` provided by `combine` - /// cannot be used. Because of this we need to use the `Parser` and the - /// error messages are different. - #[test] - fn test_error_unexpected_character() { - let expected_error; - #[cfg(feature = "std")] - { - expected_error = Err( - "Parse error at line: 2, column: 1\nUnexpected `^`\nExpected letter or digit, whitespaces, `r`, `-`, `+`, `[` or end of input\n".to_string() - ); - } - #[cfg(not(feature = "std"))] - { - expected_error = Err("unexpected parse".to_string()); - } - // Unexpected character at end of input. - assert_eq!(parse("exit\n^"), expected_error); - } - - #[test] - fn test_initial_whitespace() { - assert_eq!( - parse( - " - exit" - ), - Ok(vec![Instruction { - name: "exit".to_string(), - operands: vec![], - }]) - ); - } -} diff --git a/kernel/crates/rbpf/src/assembler.rs b/kernel/crates/rbpf/src/assembler.rs deleted file mode 100644 index bf452b1e..00000000 --- a/kernel/crates/rbpf/src/assembler.rs +++ /dev/null @@ -1,277 +0,0 @@ -// SPDX-License-Identifier: (Apache-2.0 OR MIT) -// Copyright 2017 Rich Lane - -//! This module translates eBPF assembly language to binary. - -use alloc::{ - collections::BTreeMap, - format, - string::{String, ToString}, - vec, - vec::Vec, -}; - -use self::InstructionType::{ - AluBinary, AluUnary, Call, Endian, JumpConditional, JumpUnconditional, LoadAbs, LoadImm, - LoadInd, LoadReg, NoOperand, StoreImm, StoreReg, -}; -use crate::{ - asm_parser::{ - parse, Instruction, Operand, - Operand::{Integer, Memory, Nil, Register}, - }, - ebpf::{self, Insn}, -}; - -#[derive(Clone, Copy, Debug, PartialEq)] -enum InstructionType { - AluBinary, - AluUnary, - LoadImm, - LoadAbs, - LoadInd, - LoadReg, - StoreImm, - StoreReg, - JumpUnconditional, - JumpConditional, - Call, - Endian(i64), - NoOperand, -} - -fn make_instruction_map() -> BTreeMap { - let mut result = BTreeMap::new(); - - let alu_binary_ops = [ - ("add", ebpf::BPF_ADD), - ("sub", ebpf::BPF_SUB), - ("mul", ebpf::BPF_MUL), - ("div", ebpf::BPF_DIV), - ("or", ebpf::BPF_OR), - ("and", ebpf::BPF_AND), - ("lsh", ebpf::BPF_LSH), - ("rsh", ebpf::BPF_RSH), - ("mod", ebpf::BPF_MOD), - ("xor", ebpf::BPF_XOR), - ("mov", ebpf::BPF_MOV), - ("arsh", ebpf::BPF_ARSH), - ]; - - let mem_sizes = [ - ("w", ebpf::BPF_W), - ("h", ebpf::BPF_H), - ("b", ebpf::BPF_B), - ("dw", ebpf::BPF_DW), - ]; - - let jump_conditions = [ - ("jeq", ebpf::BPF_JEQ), - ("jgt", ebpf::BPF_JGT), - ("jge", ebpf::BPF_JGE), - ("jlt", ebpf::BPF_JLT), - ("jle", ebpf::BPF_JLE), - ("jset", ebpf::BPF_JSET), - ("jne", ebpf::BPF_JNE), - ("jsgt", ebpf::BPF_JSGT), - ("jsge", ebpf::BPF_JSGE), - ("jslt", ebpf::BPF_JSLT), - ("jsle", ebpf::BPF_JSLE), - ]; - - { - let mut entry = |name: &str, inst_type: InstructionType, opc: u8| { - result.insert(name.to_string(), (inst_type, opc)) - }; - - // Miscellaneous. - entry("exit", NoOperand, ebpf::EXIT); - entry("ja", JumpUnconditional, ebpf::JA); - entry("call", Call, ebpf::CALL); - entry("lddw", LoadImm, ebpf::LD_DW_IMM); - - // AluUnary. - entry("neg", AluUnary, ebpf::NEG64); - entry("neg32", AluUnary, ebpf::NEG32); - entry("neg64", AluUnary, ebpf::NEG64); - - // AluBinary. - for &(name, opc) in &alu_binary_ops { - entry(name, AluBinary, ebpf::BPF_ALU64 | opc); - entry(&format!("{name}32"), AluBinary, ebpf::BPF_ALU | opc); - entry(&format!("{name}64"), AluBinary, ebpf::BPF_ALU64 | opc); - } - - // LoadAbs, LoadInd, LoadReg, StoreImm, and StoreReg. - for &(suffix, size) in &mem_sizes { - entry( - &format!("ldabs{suffix}"), - LoadAbs, - ebpf::BPF_ABS | ebpf::BPF_LD | size, - ); - entry( - &format!("ldind{suffix}"), - LoadInd, - ebpf::BPF_IND | ebpf::BPF_LD | size, - ); - entry( - &format!("ldx{suffix}"), - LoadReg, - ebpf::BPF_MEM | ebpf::BPF_LDX | size, - ); - entry( - &format!("st{suffix}"), - StoreImm, - ebpf::BPF_MEM | ebpf::BPF_ST | size, - ); - entry( - &format!("stx{suffix}"), - StoreReg, - ebpf::BPF_MEM | ebpf::BPF_STX | size, - ); - } - - // JumpConditional. - for &(name, condition) in &jump_conditions { - entry(name, JumpConditional, ebpf::BPF_JMP | condition); - entry( - &format!("{name}32"), - JumpConditional, - ebpf::BPF_JMP32 | condition, - ); - } - - // Endian. - for &size in &[16, 32, 64] { - entry(&format!("be{size}"), Endian(size), ebpf::BE); - entry(&format!("le{size}"), Endian(size), ebpf::LE); - } - } - - result -} - -fn insn(opc: u8, dst: i64, src: i64, off: i64, imm: i64) -> Result { - if !(0..16).contains(&dst) { - return Err(format!("Invalid destination register {dst}")); - } - if dst < 0 || src >= 16 { - return Err(format!("Invalid source register {src}")); - } - if !(-32768..32768).contains(&off) { - return Err(format!("Invalid offset {off}")); - } - if !(-2147483648..2147483648).contains(&imm) { - return Err(format!("Invalid immediate {imm}")); - } - Ok(Insn { - opc, - dst: dst as u8, - src: src as u8, - off: off as i16, - imm: imm as i32, - }) -} - -// TODO Use slice patterns when available and remove this function. -fn operands_tuple(operands: &[Operand]) -> Result<(Operand, Operand, Operand), String> { - match operands.len() { - 0 => Ok((Nil, Nil, Nil)), - 1 => Ok((operands[0], Nil, Nil)), - 2 => Ok((operands[0], operands[1], Nil)), - 3 => Ok((operands[0], operands[1], operands[2])), - _ => Err("Too many operands".to_string()), - } -} - -fn encode(inst_type: InstructionType, opc: u8, operands: &[Operand]) -> Result { - let (a, b, c) = (operands_tuple(operands))?; - match (inst_type, a, b, c) { - (AluBinary, Register(dst), Register(src), Nil) => insn(opc | ebpf::BPF_X, dst, src, 0, 0), - (AluBinary, Register(dst), Integer(imm), Nil) => insn(opc | ebpf::BPF_K, dst, 0, 0, imm), - (AluUnary, Register(dst), Nil, Nil) => insn(opc, dst, 0, 0, 0), - (LoadAbs, Integer(imm), Nil, Nil) => insn(opc, 0, 0, 0, imm), - (LoadInd, Register(src), Integer(imm), Nil) => insn(opc, 0, src, 0, imm), - (LoadReg, Register(dst), Memory(src, off), Nil) - | (StoreReg, Memory(dst, off), Register(src), Nil) => insn(opc, dst, src, off, 0), - (StoreImm, Memory(dst, off), Integer(imm), Nil) => insn(opc, dst, 0, off, imm), - (NoOperand, Nil, Nil, Nil) => insn(opc, 0, 0, 0, 0), - (JumpUnconditional, Integer(off), Nil, Nil) => insn(opc, 0, 0, off, 0), - (JumpConditional, Register(dst), Register(src), Integer(off)) => { - insn(opc | ebpf::BPF_X, dst, src, off, 0) - } - (JumpConditional, Register(dst), Integer(imm), Integer(off)) => { - insn(opc | ebpf::BPF_K, dst, 0, off, imm) - } - (Call, Integer(imm), Nil, Nil) => insn(opc, 0, 0, 0, imm), - (Endian(size), Register(dst), Nil, Nil) => insn(opc, dst, 0, 0, size), - (LoadImm, Register(dst), Integer(imm), Nil) => insn(opc, dst, 0, 0, (imm << 32) >> 32), - _ => Err(format!("Unexpected operands: {operands:?}")), - } -} - -fn assemble_internal(parsed: &[Instruction]) -> Result, String> { - let instruction_map = make_instruction_map(); - let mut result: Vec = vec![]; - for instruction in parsed { - let name = instruction.name.as_str(); - match instruction_map.get(name) { - Some(&(inst_type, opc)) => { - match encode(inst_type, opc, &instruction.operands) { - Ok(insn) => result.push(insn), - Err(msg) => return Err(format!("Failed to encode {name}: {msg}")), - } - // Special case for lddw. - if let LoadImm = inst_type { - if let Integer(imm) = instruction.operands[1] { - result.push(insn(0, 0, 0, 0, imm >> 32).unwrap()); - } - } - } - None => return Err(format!("Invalid instruction {name:?}")), - } - } - Ok(result) -} - -/// Parse assembly source and translate to binary. -/// -/// # Examples -/// -/// ``` -/// use rbpf::assembler::assemble; -/// let prog = assemble("add64 r1, 0x605 -/// mov64 r2, 0x32 -/// mov64 r1, r0 -/// be16 r0 -/// neg64 r2 -/// exit"); -/// println!("{:?}", prog); -/// # assert_eq!(prog, -/// # Ok(vec![0x07, 0x01, 0x00, 0x00, 0x05, 0x06, 0x00, 0x00, -/// # 0xb7, 0x02, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, -/// # 0xbf, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/// # 0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, -/// # 0x87, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/// # 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])); -/// ``` -/// -/// This will produce the following output: -/// -/// ```test -/// Ok([0x07, 0x01, 0x00, 0x00, 0x05, 0x06, 0x00, 0x00, -/// 0xb7, 0x02, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, -/// 0xbf, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/// 0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, -/// 0x87, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) -/// ``` -pub fn assemble(src: &str) -> Result, String> { - let parsed = (parse(src))?; - let insns = (assemble_internal(&parsed))?; - let mut result: Vec = vec![]; - for insn in insns { - result.extend_from_slice(&insn.to_array()); - } - Ok(result) -} diff --git a/kernel/crates/rbpf/src/cranelift.rs b/kernel/crates/rbpf/src/cranelift.rs deleted file mode 100644 index 10031e5b..00000000 --- a/kernel/crates/rbpf/src/cranelift.rs +++ /dev/null @@ -1,1230 +0,0 @@ -// SPDX-License-Identifier: (Apache-2.0 OR MIT) - -use alloc::{collections::BTreeMap, format, vec, vec::Vec}; -use core::{mem, mem::ManuallyDrop}; -use std::io::ErrorKind; - -use cranelift_codegen::{ - entity::EntityRef, - ir::{ - condcodes::IntCC, - types::{I16, I32, I64, I8}, - AbiParam, Block, Endianness, FuncRef, Function, InstBuilder, MemFlags, Signature, - SourceLoc, StackSlotData, StackSlotKind, TrapCode, Type, UserFuncName, Value, - }, - isa::OwnedTargetIsa, - settings::{self, Configurable}, -}; -use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext, Variable}; -use cranelift_jit::{JITBuilder, JITModule}; -use cranelift_module::{FuncId, Linkage, Module}; - -use super::{Error, HashMap, HashSet}; -use crate::ebpf::{ - self, Insn, BPF_ALU_OP_MASK, BPF_IND, BPF_JEQ, BPF_JGE, BPF_JGT, BPF_JLE, BPF_JLT, BPF_JMP32, - BPF_JNE, BPF_JSET, BPF_JSGE, BPF_JSGT, BPF_JSLE, BPF_JSLT, BPF_X, STACK_SIZE, -}; - -pub type JittedFunction = extern "C" fn( - *mut u8, // mem_ptr - usize, // mem_len - *mut u8, // mbuff_ptr - usize, // mbuff_len -) -> u64; - -pub(crate) struct CraneliftCompiler { - isa: OwnedTargetIsa, - module: JITModule, - - helpers: HashMap, - helper_func_refs: HashMap, - - /// List of blocks corresponding to each instruction. - /// We only store the first instruction that observes a new block - insn_blocks: BTreeMap, - /// Map of block targets for each jump/branching instruction. - insn_targets: BTreeMap, - filled_blocks: HashSet, - - /// Map of register numbers to Cranelift variables. - registers: [Variable; 11], - /// Other usefull variables used throughout the program. - mem_start: Variable, - mem_end: Variable, - mbuf_start: Variable, - mbuf_end: Variable, - stack_start: Variable, - stack_end: Variable, -} - -impl CraneliftCompiler { - pub(crate) fn new(helpers: HashMap) -> Self { - let mut flag_builder = settings::builder(); - - flag_builder.set("opt_level", "speed").unwrap(); - - // Enable stack probes - flag_builder.enable("enable_probestack").unwrap(); - flag_builder.set("probestack_strategy", "inline").unwrap(); - - let isa_builder = cranelift_native::builder().unwrap_or_else(|msg| { - panic!("host machine is not supported: {}", msg); - }); - let isa = isa_builder - .finish(settings::Flags::new(flag_builder)) - .unwrap(); - - let mut jit_builder = - JITBuilder::with_isa(isa.clone(), cranelift_module::default_libcall_names()); - // Register all the helpers - for (k, v) in helpers.iter() { - let name = format!("helper_{}", k); - jit_builder.symbol(name, (*v) as usize as *const u8); - } - - let mut module = JITModule::new(jit_builder); - - let registers = (0..11) - .map(|i| Variable::new(i)) - .collect::>() - .try_into() - .unwrap(); - - Self { - isa, - module, - helpers, - helper_func_refs: HashMap::new(), - insn_blocks: BTreeMap::new(), - insn_targets: BTreeMap::new(), - filled_blocks: HashSet::new(), - registers, - mem_start: Variable::new(11), - mem_end: Variable::new(12), - mbuf_start: Variable::new(13), - mbuf_end: Variable::new(14), - stack_start: Variable::new(15), - stack_end: Variable::new(16), - } - } - - pub(crate) fn compile_function(mut self, prog: &[u8]) -> Result { - let name = "main"; - // This is not a standard eBPF function! We use an informal ABI with just 4 parameters. - // See [JittedFunction] which is the signature of this function. - // - // Since this function only serves as the entrypoint for the JITed program, it doesen't - // really matter. - let sig = Signature { - params: vec![ - AbiParam::new(I64), - AbiParam::new(I64), - AbiParam::new(I64), - AbiParam::new(I64), - ], - returns: vec![AbiParam::new(I64)], - call_conv: self.isa.default_call_conv(), - }; - - let func_id = self - .module - .declare_function(name, Linkage::Local, &sig) - .unwrap(); - - let mut ctx = self.module.make_context(); - ctx.func = Function::with_name_signature(UserFuncName::testcase(name.as_bytes()), sig); - let mut func_ctx = FunctionBuilderContext::new(); - - { - let mut builder: FunctionBuilder = FunctionBuilder::new(&mut ctx.func, &mut func_ctx); - - let entry = builder.create_block(); - builder.append_block_params_for_function_params(entry); - builder.switch_to_block(entry); - - self.build_cfg(&mut builder, prog)?; - self.build_function_prelude(&mut builder, entry)?; - self.translate_program(&mut builder, prog)?; - - builder.seal_all_blocks(); - builder.finalize(); - } - - self.module.define_function(func_id, &mut ctx).unwrap(); - self.module.finalize_definitions().unwrap(); - self.module.clear_context(&mut ctx); - - Ok(CraneliftProgram::new(self.module, func_id)) - } - - fn build_function_prelude( - &mut self, - bcx: &mut FunctionBuilder, - entry: Block, - ) -> Result<(), Error> { - // Register the VM registers as variables - for var in self.registers.iter() { - bcx.declare_var(*var, I64); - } - - // Register the bounds check variables - bcx.declare_var(self.mem_start, I64); - bcx.declare_var(self.mem_end, I64); - bcx.declare_var(self.mbuf_start, I64); - bcx.declare_var(self.mbuf_end, I64); - bcx.declare_var(self.stack_start, I64); - bcx.declare_var(self.stack_end, I64); - - // Register the helpers - for (k, _) in self.helpers.iter() { - let name = format!("helper_{}", k); - let sig = Signature { - params: vec![ - AbiParam::new(I64), - AbiParam::new(I64), - AbiParam::new(I64), - AbiParam::new(I64), - AbiParam::new(I64), - ], - returns: vec![AbiParam::new(I64)], - call_conv: self.isa.default_call_conv(), - }; - let func_id = self - .module - .declare_function(&name, Linkage::Import, &sig) - .unwrap(); - - let func_ref = self.module.declare_func_in_func(func_id, bcx.func); - self.helper_func_refs.insert(*k, func_ref); - } - - // Register the stack - let ss = bcx.create_sized_stack_slot(StackSlotData { - kind: StackSlotKind::ExplicitSlot, - size: STACK_SIZE as u32, - }); - let addr_ty = self.isa.pointer_type(); - let stack_addr = bcx.ins().stack_addr(addr_ty, ss, STACK_SIZE as i32); - bcx.def_var(self.registers[10], stack_addr); - - // Initialize the bounds check variables - let stack_start = bcx.ins().stack_addr(addr_ty, ss, 0); - bcx.def_var(self.stack_start, stack_start); - let stack_end = bcx.ins().stack_addr(addr_ty, ss, STACK_SIZE as i32); - bcx.def_var(self.stack_end, stack_end); - - // This is our internal ABI where the first 2 params are the memory - let mem_start = bcx.block_params(entry)[0]; - let mem_len = bcx.block_params(entry)[1]; - let mem_end = bcx.ins().iadd(mem_start, mem_len); - bcx.def_var(self.mem_start, mem_start); - bcx.def_var(self.mem_end, mem_end); - - // And the next 2 are the mbuf - let mbuf_start = bcx.block_params(entry)[2]; - let mbuf_len = bcx.block_params(entry)[3]; - let mbuf_end = bcx.ins().iadd(mbuf_start, mbuf_len); - bcx.def_var(self.mbuf_start, mbuf_start); - bcx.def_var(self.mbuf_end, mbuf_end); - - // The ABI for eBPF specifies that R1 must contain either the memory, or mbuff pointer - // If the mbuf length is non-zero, then we use that, otherwise we use the memory pointer - let mbuf_exists = bcx.ins().icmp_imm(IntCC::NotEqual, mbuf_len, 0); - let mem_or_mbuf = bcx.ins().select(mbuf_exists, mbuf_start, mem_start); - bcx.def_var(self.registers[1], mem_or_mbuf); - - // R2 should contain the length of the memory or mbuf - // At least ebpf-conformance tests expect this - let mem_or_mbuf_len = bcx.ins().select(mbuf_exists, mbuf_len, mem_len); - bcx.def_var(self.registers[2], mem_or_mbuf_len); - - // Insert the *actual* initial block - let program_entry = bcx.create_block(); - bcx.ins().jump(program_entry, &[]); - self.filled_blocks.insert(bcx.current_block().unwrap()); - self.insn_blocks.insert(0, program_entry); - - Ok(()) - } - - fn translate_program(&mut self, bcx: &mut FunctionBuilder, prog: &[u8]) -> Result<(), Error> { - let mut insn_ptr: usize = 0; - while insn_ptr * ebpf::INSN_SIZE < prog.len() { - let insn = ebpf::get_insn(prog, insn_ptr); - - // If this instruction is on a new block switch to it. - if let Some(block) = self.insn_blocks.get(&(insn_ptr as u32)) { - // Blocks must have a terminator instruction at the end before we switch away from them - let current_block = bcx.current_block().unwrap(); - if !self.filled_blocks.contains(¤t_block) { - bcx.ins().jump(*block, &[]); - } - - bcx.switch_to_block(*block); - } - - // Set the source location for the instruction - bcx.set_srcloc(SourceLoc::new(insn_ptr as u32)); - - match insn.opc { - // BPF_LD class - // LD_ABS_* and LD_IND_* are supposed to load pointer to data from metadata buffer. - // Since this pointer is constant, and since we already know it (mem), do not - // bother re-fetching it, just use mem already. - ebpf::LD_ABS_B - | ebpf::LD_ABS_H - | ebpf::LD_ABS_W - | ebpf::LD_ABS_DW - | ebpf::LD_IND_B - | ebpf::LD_IND_H - | ebpf::LD_IND_W - | ebpf::LD_IND_DW => { - let ty = match insn.opc { - ebpf::LD_ABS_B | ebpf::LD_IND_B => I8, - ebpf::LD_ABS_H | ebpf::LD_IND_H => I16, - ebpf::LD_ABS_W | ebpf::LD_IND_W => I32, - ebpf::LD_ABS_DW | ebpf::LD_IND_DW => I64, - _ => unreachable!(), - }; - - // Both instructions add the imm part of the instruction to the pointer - let ptr = bcx.use_var(self.mem_start); - let offset = bcx - .ins() - .iconst(self.isa.pointer_type(), insn.imm as u32 as i64); - let addr = bcx.ins().iadd(ptr, offset); - - // IND instructions additionally add the value of the source register - let is_ind = (insn.opc & BPF_IND) != 0; - let addr = if is_ind { - let src_reg = self.insn_src(bcx, &insn); - bcx.ins().iadd(addr, src_reg) - } else { - addr - }; - - // The offset here has already been added to the pointer, so we pass 0 - let loaded = self.reg_load(bcx, ty, addr, 0); - - let ext = if ty != I64 { - bcx.ins().uextend(I64, loaded) - } else { - loaded - }; - - self.set_dst(bcx, &insn, ext); - } - ebpf::LD_DW_IMM => { - insn_ptr += 1; - let next_insn = ebpf::get_insn(prog, insn_ptr); - - let imm = (((insn.imm as u32) as u64) + ((next_insn.imm as u64) << 32)) as i64; - let iconst = bcx.ins().iconst(I64, imm); - self.set_dst(bcx, &insn, iconst); - } - - // BPF_LDX class - ebpf::LD_B_REG | ebpf::LD_H_REG | ebpf::LD_W_REG | ebpf::LD_DW_REG => { - let ty = match insn.opc { - ebpf::LD_B_REG => I8, - ebpf::LD_H_REG => I16, - ebpf::LD_W_REG => I32, - ebpf::LD_DW_REG => I64, - _ => unreachable!(), - }; - - let base = self.insn_src(bcx, &insn); - let loaded = self.reg_load(bcx, ty, base, insn.off); - - let ext = if ty != I64 { - bcx.ins().uextend(I64, loaded) - } else { - loaded - }; - - self.set_dst(bcx, &insn, ext); - } - - // BPF_ST and BPF_STX class - ebpf::ST_B_IMM - | ebpf::ST_H_IMM - | ebpf::ST_W_IMM - | ebpf::ST_DW_IMM - | ebpf::ST_B_REG - | ebpf::ST_H_REG - | ebpf::ST_W_REG - | ebpf::ST_DW_REG => { - let ty = match insn.opc { - ebpf::ST_B_IMM | ebpf::ST_B_REG => I8, - ebpf::ST_H_IMM | ebpf::ST_H_REG => I16, - ebpf::ST_W_IMM | ebpf::ST_W_REG => I32, - ebpf::ST_DW_IMM | ebpf::ST_DW_REG => I64, - _ => unreachable!(), - }; - let is_imm = match insn.opc { - ebpf::ST_B_IMM | ebpf::ST_H_IMM | ebpf::ST_W_IMM | ebpf::ST_DW_IMM => true, - ebpf::ST_B_REG | ebpf::ST_H_REG | ebpf::ST_W_REG | ebpf::ST_DW_REG => false, - _ => unreachable!(), - }; - - let value = if is_imm { - self.insn_imm64(bcx, &insn) - } else { - self.insn_src(bcx, &insn) - }; - - let narrow = if ty != I64 { - bcx.ins().ireduce(ty, value) - } else { - value - }; - - let base = self.insn_dst(bcx, &insn); - self.reg_store(bcx, ty, base, insn.off, narrow); - } - - ebpf::ST_W_XADD => unimplemented!(), - ebpf::ST_DW_XADD => unimplemented!(), - - // BPF_ALU class - // TODO Check how overflow works in kernel. Should we &= U32MAX all src register value - // before we do the operation? - // Cf ((0x11 << 32) - (0x1 << 32)) as u32 VS ((0x11 << 32) as u32 - (0x1 << 32) as u32 - ebpf::ADD32_IMM => { - let src = self.insn_dst32(bcx, &insn); - let imm = self.insn_imm32(bcx, &insn); - let res = bcx.ins().iadd(src, imm); - self.set_dst32(bcx, &insn, res); - } - ebpf::ADD32_REG => { - //((reg[_dst] & U32MAX) + (reg[_src] & U32MAX)) & U32MAX, - let lhs = self.insn_dst32(bcx, &insn); - let rhs = self.insn_src32(bcx, &insn); - let res = bcx.ins().iadd(lhs, rhs); - self.set_dst32(bcx, &insn, res); - } - ebpf::SUB32_IMM => { - // reg[_dst] = (reg[_dst] as i32).wrapping_sub(insn.imm) as u64, - let src = self.insn_dst32(bcx, &insn); - let imm = self.insn_imm32(bcx, &insn); - let res = bcx.ins().isub(src, imm); - self.set_dst32(bcx, &insn, res); - } - ebpf::SUB32_REG => { - // reg[_dst] = (reg[_dst] as i32).wrapping_sub(reg[_src] as i32) as u64, - let lhs = self.insn_dst32(bcx, &insn); - let rhs = self.insn_src32(bcx, &insn); - let res = bcx.ins().isub(lhs, rhs); - self.set_dst32(bcx, &insn, res); - } - ebpf::MUL32_IMM => { - // reg[_dst] = (reg[_dst] as i32).wrapping_mul(insn.imm) as u64, - let src = self.insn_dst32(bcx, &insn); - let imm = self.insn_imm32(bcx, &insn); - let res = bcx.ins().imul(src, imm); - self.set_dst32(bcx, &insn, res); - } - ebpf::MUL32_REG => { - // reg[_dst] = (reg[_dst] as i32).wrapping_mul(reg[_src] as i32) as u64, - let lhs = self.insn_dst32(bcx, &insn); - let rhs = self.insn_src32(bcx, &insn); - let res = bcx.ins().imul(lhs, rhs); - self.set_dst32(bcx, &insn, res); - } - ebpf::DIV32_IMM => { - // reg[_dst] = (reg[_dst] as u32 / insn.imm as u32) as u64, - let res = if insn.imm == 0 { - bcx.ins().iconst(I32, 0) - } else { - let imm = self.insn_imm32(bcx, &insn); - let src = self.insn_dst32(bcx, &insn); - bcx.ins().udiv(src, imm) - }; - self.set_dst32(bcx, &insn, res); - } - ebpf::DIV32_REG => { - // reg[_dst] = (reg[_dst] as u32 / reg[_src] as u32) as u64, - let zero = bcx.ins().iconst(I32, 0); - let one = bcx.ins().iconst(I32, 1); - - let lhs = self.insn_dst32(bcx, &insn); - let rhs = self.insn_src32(bcx, &insn); - - let rhs_is_zero = bcx.ins().icmp(IntCC::Equal, rhs, zero); - let safe_rhs = bcx.ins().select(rhs_is_zero, one, rhs); - let div_res = bcx.ins().udiv(lhs, safe_rhs); - - let res = bcx.ins().select(rhs_is_zero, zero, div_res); - self.set_dst32(bcx, &insn, res); - } - ebpf::OR32_IMM => { - // reg[_dst] = (reg[_dst] as u32 | insn.imm as u32) as u64, - let src = self.insn_dst32(bcx, &insn); - let imm = self.insn_imm32(bcx, &insn); - let res = bcx.ins().bor(src, imm); - self.set_dst32(bcx, &insn, res); - } - ebpf::OR32_REG => { - // reg[_dst] = (reg[_dst] as u32 | reg[_src] as u32) as u64, - let lhs = self.insn_dst32(bcx, &insn); - let rhs = self.insn_src32(bcx, &insn); - let res = bcx.ins().bor(lhs, rhs); - self.set_dst32(bcx, &insn, res); - } - ebpf::AND32_IMM => { - // reg[_dst] = (reg[_dst] as u32 & insn.imm as u32) as u64, - let src = self.insn_dst32(bcx, &insn); - let imm = self.insn_imm32(bcx, &insn); - let res = bcx.ins().band(src, imm); - self.set_dst32(bcx, &insn, res); - } - ebpf::AND32_REG => { - // reg[_dst] = (reg[_dst] as u32 & reg[_src] as u32) as u64, - let lhs = self.insn_dst32(bcx, &insn); - let rhs = self.insn_src32(bcx, &insn); - let res = bcx.ins().band(lhs, rhs); - self.set_dst32(bcx, &insn, res); - } - ebpf::LSH32_IMM => { - // reg[_dst] = (reg[_dst] as u32).wrapping_shl(insn.imm as u32) as u64, - let src = self.insn_dst32(bcx, &insn); - let imm = self.insn_imm32(bcx, &insn); - let res = bcx.ins().ishl(src, imm); - self.set_dst32(bcx, &insn, res); - } - ebpf::LSH32_REG => { - // reg[_dst] = (reg[_dst] as u32).wrapping_shl(reg[_src] as u32) as u64, - let lhs = self.insn_dst32(bcx, &insn); - let rhs = self.insn_src32(bcx, &insn); - let res = bcx.ins().ishl(lhs, rhs); - self.set_dst32(bcx, &insn, res); - } - ebpf::RSH32_IMM => { - // reg[_dst] = (reg[_dst] as u32).wrapping_shr(insn.imm as u32) as u64, - let src = self.insn_dst32(bcx, &insn); - let imm = self.insn_imm32(bcx, &insn); - let res = bcx.ins().ushr(src, imm); - self.set_dst32(bcx, &insn, res); - } - ebpf::RSH32_REG => { - // reg[_dst] = (reg[_dst] as u32).wrapping_shr(reg[_src] as u32) as u64, - let lhs = self.insn_dst32(bcx, &insn); - let rhs = self.insn_src32(bcx, &insn); - let res = bcx.ins().ushr(lhs, rhs); - self.set_dst32(bcx, &insn, res); - } - ebpf::NEG32 => { - // { reg[_dst] = (reg[_dst] as i32).wrapping_neg() as u64; reg[_dst] &= U32MAX; }, - let src = self.insn_dst32(bcx, &insn); - let res = bcx.ins().ineg(src); - // TODO: Do we need to mask the result? - self.set_dst32(bcx, &insn, res); - } - ebpf::MOD32_IMM => { - // reg[_dst] = (reg[_dst] as u32 % insn.imm as u32) as u64, - - if insn.imm != 0 { - let imm = self.insn_imm32(bcx, &insn); - let src = self.insn_dst32(bcx, &insn); - let res = bcx.ins().urem(src, imm); - self.set_dst32(bcx, &insn, res); - } - } - ebpf::MOD32_REG => { - // reg[_dst] = (reg[_dst] as u32 % reg[_src] as u32) as u64, - let zero = bcx.ins().iconst(I32, 0); - let one = bcx.ins().iconst(I32, 1); - - let lhs = self.insn_dst32(bcx, &insn); - let rhs = self.insn_src32(bcx, &insn); - - let rhs_is_zero = bcx.ins().icmp(IntCC::Equal, rhs, zero); - let safe_rhs = bcx.ins().select(rhs_is_zero, one, rhs); - let div_res = bcx.ins().urem(lhs, safe_rhs); - - let res = bcx.ins().select(rhs_is_zero, lhs, div_res); - self.set_dst32(bcx, &insn, res); - } - ebpf::XOR32_IMM => { - // reg[_dst] = (reg[_dst] as u32 ^ insn.imm as u32) as u64, - let src = self.insn_dst32(bcx, &insn); - let imm = self.insn_imm32(bcx, &insn); - let res = bcx.ins().bxor(src, imm); - self.set_dst32(bcx, &insn, res); - } - ebpf::XOR32_REG => { - // reg[_dst] = (reg[_dst] as u32 ^ reg[_src] as u32) as u64, - let lhs = self.insn_dst32(bcx, &insn); - let rhs = self.insn_src32(bcx, &insn); - let res = bcx.ins().bxor(lhs, rhs); - self.set_dst32(bcx, &insn, res); - } - ebpf::MOV32_IMM => { - let imm = self.insn_imm32(bcx, &insn); - self.set_dst32(bcx, &insn, imm); - } - ebpf::MOV32_REG => { - // reg[_dst] = (reg[_src] as u32) as u64, - let src = self.insn_src32(bcx, &insn); - self.set_dst32(bcx, &insn, src); - } - ebpf::ARSH32_IMM => { - // { reg[_dst] = (reg[_dst] as i32).wrapping_shr(insn.imm as u32) as u64; reg[_dst] &= U32MAX; }, - let src = self.insn_dst32(bcx, &insn); - let imm = self.insn_imm32(bcx, &insn); - let res = bcx.ins().sshr(src, imm); - self.set_dst32(bcx, &insn, res); - } - ebpf::ARSH32_REG => { - // { reg[_dst] = (reg[_dst] as i32).wrapping_shr(reg[_src] as u32) as u64; reg[_dst] &= U32MAX; }, - let lhs = self.insn_dst32(bcx, &insn); - let rhs = self.insn_src32(bcx, &insn); - let res = bcx.ins().sshr(lhs, rhs); - self.set_dst32(bcx, &insn, res); - } - - ebpf::BE | ebpf::LE => { - let should_swap = match insn.opc { - ebpf::BE => self.isa.endianness() == Endianness::Little, - ebpf::LE => self.isa.endianness() == Endianness::Big, - _ => unreachable!(), - }; - - let ty: Type = match insn.imm { - 16 => I16, - 32 => I32, - 64 => I64, - _ => unreachable!(), - }; - - if should_swap { - let src = self.insn_dst(bcx, &insn); - let src_narrow = if ty != I64 { - bcx.ins().ireduce(ty, src) - } else { - src - }; - - let res = bcx.ins().bswap(src_narrow); - let res_wide = if ty != I64 { - bcx.ins().uextend(I64, res) - } else { - res - }; - - self.set_dst(bcx, &insn, res_wide); - } - } - - // BPF_ALU64 class - ebpf::ADD64_IMM => { - // reg[_dst] = reg[_dst].wrapping_add(insn.imm as u64), - let imm = self.insn_imm64(bcx, &insn); - let src = self.insn_dst(bcx, &insn); - let res = bcx.ins().iadd(src, imm); - self.set_dst(bcx, &insn, res); - } - ebpf::ADD64_REG => { - // reg[_dst] = reg[_dst].wrapping_add(reg[_src]), - let lhs = self.insn_dst(bcx, &insn); - let rhs = self.insn_src(bcx, &insn); - let res = bcx.ins().iadd(lhs, rhs); - self.set_dst(bcx, &insn, res); - } - ebpf::SUB64_IMM => { - // reg[_dst] = reg[_dst].wrapping_sub(insn.imm as u64), - let imm = self.insn_imm64(bcx, &insn); - let src = self.insn_dst(bcx, &insn); - let res = bcx.ins().isub(src, imm); - self.set_dst(bcx, &insn, res); - } - ebpf::SUB64_REG => { - // reg[_dst] = reg[_dst].wrapping_sub(reg[_src]), - let lhs = self.insn_dst(bcx, &insn); - let rhs = self.insn_src(bcx, &insn); - let res = bcx.ins().isub(lhs, rhs); - self.set_dst(bcx, &insn, res); - } - ebpf::MUL64_IMM => { - // reg[_dst] = reg[_dst].wrapping_mul(insn.imm as u64), - let imm = self.insn_imm64(bcx, &insn); - let src = self.insn_dst(bcx, &insn); - let res = bcx.ins().imul(src, imm); - self.set_dst(bcx, &insn, res); - } - ebpf::MUL64_REG => { - // reg[_dst] = reg[_dst].wrapping_mul(reg[_src]), - let lhs = self.insn_dst(bcx, &insn); - let rhs = self.insn_src(bcx, &insn); - let res = bcx.ins().imul(lhs, rhs); - self.set_dst(bcx, &insn, res); - } - ebpf::DIV64_IMM => { - // reg[_dst] /= insn.imm as u64, - let res = if insn.imm == 0 { - bcx.ins().iconst(I64, 0) - } else { - let imm = self.insn_imm64(bcx, &insn); - let src = self.insn_dst(bcx, &insn); - bcx.ins().udiv(src, imm) - }; - self.set_dst(bcx, &insn, res); - } - ebpf::DIV64_REG => { - // reg[_dst] /= reg[_src], if reg[_src] != 0 - // reg[_dst] = 0, if reg[_src] == 0 - let zero = bcx.ins().iconst(I64, 0); - let one = bcx.ins().iconst(I64, 1); - - let lhs = self.insn_dst(bcx, &insn); - let rhs = self.insn_src(bcx, &insn); - - let rhs_is_zero = bcx.ins().icmp(IntCC::Equal, rhs, zero); - let safe_rhs = bcx.ins().select(rhs_is_zero, one, rhs); - let div_res = bcx.ins().udiv(lhs, safe_rhs); - - let res = bcx.ins().select(rhs_is_zero, zero, div_res); - self.set_dst(bcx, &insn, res); - } - ebpf::MOD64_IMM => { - // reg[_dst] %= insn.imm as u64, - - if insn.imm != 0 { - let imm = self.insn_imm64(bcx, &insn); - let src = self.insn_dst(bcx, &insn); - let res = bcx.ins().urem(src, imm); - self.set_dst(bcx, &insn, res); - }; - } - ebpf::MOD64_REG => { - // reg[_dst] %= reg[_src], if reg[_src] != 0 - - let zero = bcx.ins().iconst(I64, 0); - let one = bcx.ins().iconst(I64, 1); - - let lhs = self.insn_dst(bcx, &insn); - let rhs = self.insn_src(bcx, &insn); - - let rhs_is_zero = bcx.ins().icmp(IntCC::Equal, rhs, zero); - let safe_rhs = bcx.ins().select(rhs_is_zero, one, rhs); - let div_res = bcx.ins().urem(lhs, safe_rhs); - - let res = bcx.ins().select(rhs_is_zero, lhs, div_res); - self.set_dst(bcx, &insn, res); - } - ebpf::OR64_IMM => { - // reg[_dst] |= insn.imm as u64, - let imm = self.insn_imm64(bcx, &insn); - let src = self.insn_dst(bcx, &insn); - let res = bcx.ins().bor(src, imm); - self.set_dst(bcx, &insn, res); - } - ebpf::OR64_REG => { - // reg[_dst] |= reg[_src], - let lhs = self.insn_dst(bcx, &insn); - let rhs = self.insn_src(bcx, &insn); - let res = bcx.ins().bor(lhs, rhs); - self.set_dst(bcx, &insn, res); - } - ebpf::AND64_IMM => { - // reg[_dst] &= insn.imm as u64, - let imm = self.insn_imm64(bcx, &insn); - let src = self.insn_dst(bcx, &insn); - let res = bcx.ins().band(src, imm); - self.set_dst(bcx, &insn, res); - } - ebpf::AND64_REG => { - // reg[_dst] &= reg[_src], - let lhs = self.insn_dst(bcx, &insn); - let rhs = self.insn_src(bcx, &insn); - let res = bcx.ins().band(lhs, rhs); - self.set_dst(bcx, &insn, res); - } - ebpf::LSH64_IMM => { - // reg[_dst] <<= insn.imm as u64, - let imm = self.insn_imm64(bcx, &insn); - let src = self.insn_dst(bcx, &insn); - let res = bcx.ins().ishl(src, imm); - self.set_dst(bcx, &insn, res); - } - ebpf::LSH64_REG => { - // reg[_dst] <<= reg[_src], - let lhs = self.insn_dst(bcx, &insn); - let rhs = self.insn_src(bcx, &insn); - let res = bcx.ins().ishl(lhs, rhs); - self.set_dst(bcx, &insn, res); - } - ebpf::RSH64_IMM => { - // reg[_dst] >>= insn.imm as u64, - let imm = self.insn_imm64(bcx, &insn); - let src = self.insn_dst(bcx, &insn); - let res = bcx.ins().ushr(src, imm); - self.set_dst(bcx, &insn, res); - } - ebpf::RSH64_REG => { - // reg[_dst] >>= reg[_src], - let lhs = self.insn_dst(bcx, &insn); - let rhs = self.insn_src(bcx, &insn); - let res = bcx.ins().ushr(lhs, rhs); - self.set_dst(bcx, &insn, res); - } - ebpf::NEG64 => { - // reg[_dst] = -(reg[_dst] as i64) as u64, - let src = self.insn_dst(bcx, &insn); - let res = bcx.ins().ineg(src); - self.set_dst(bcx, &insn, res); - } - ebpf::XOR64_IMM => { - // reg[_dst] ^= insn.imm as u64, - let imm = self.insn_imm64(bcx, &insn); - let src = self.insn_dst(bcx, &insn); - let res = bcx.ins().bxor(src, imm); - self.set_dst(bcx, &insn, res); - } - ebpf::XOR64_REG => { - // reg[_dst] ^= reg[_src], - let lhs = self.insn_dst(bcx, &insn); - let rhs = self.insn_src(bcx, &insn); - let res = bcx.ins().bxor(lhs, rhs); - self.set_dst(bcx, &insn, res); - } - ebpf::MOV64_IMM => { - // reg[_dst] = insn.imm as u64, - let imm = self.insn_imm64(bcx, &insn); - bcx.def_var(self.registers[insn.dst as usize], imm); - } - ebpf::MOV64_REG => { - // reg[_dst] = reg[_src], - let src = self.insn_src(bcx, &insn); - bcx.def_var(self.registers[insn.dst as usize], src); - } - ebpf::ARSH64_IMM => { - // reg[_dst] = (reg[_dst] as i64 >> insn.imm) as u64, - let imm = self.insn_imm64(bcx, &insn); - let src = self.insn_dst(bcx, &insn); - let res = bcx.ins().sshr(src, imm); - self.set_dst(bcx, &insn, res); - } - ebpf::ARSH64_REG => { - // reg[_dst] = (reg[_dst] as i64 >> reg[_src]) as u64, - let lhs = self.insn_dst(bcx, &insn); - let rhs = self.insn_src(bcx, &insn); - let res = bcx.ins().sshr(lhs, rhs); - self.set_dst(bcx, &insn, res); - } - - // BPF_JMP & BPF_JMP32 class - ebpf::JA => { - let (_, target_block) = self.insn_targets[&(insn_ptr as u32)]; - - bcx.ins().jump(target_block, &[]); - self.filled_blocks.insert(bcx.current_block().unwrap()); - } - ebpf::JEQ_IMM - | ebpf::JEQ_REG - | ebpf::JGT_IMM - | ebpf::JGT_REG - | ebpf::JGE_IMM - | ebpf::JGE_REG - | ebpf::JLT_IMM - | ebpf::JLT_REG - | ebpf::JLE_IMM - | ebpf::JLE_REG - | ebpf::JNE_IMM - | ebpf::JNE_REG - | ebpf::JSGT_IMM - | ebpf::JSGT_REG - | ebpf::JSGE_IMM - | ebpf::JSGE_REG - | ebpf::JSLT_IMM - | ebpf::JSLT_REG - | ebpf::JSLE_IMM - | ebpf::JSLE_REG - | ebpf::JSET_IMM - | ebpf::JSET_REG - | ebpf::JEQ_IMM32 - | ebpf::JEQ_REG32 - | ebpf::JGT_IMM32 - | ebpf::JGT_REG32 - | ebpf::JGE_IMM32 - | ebpf::JGE_REG32 - | ebpf::JLT_IMM32 - | ebpf::JLT_REG32 - | ebpf::JLE_IMM32 - | ebpf::JLE_REG32 - | ebpf::JNE_IMM32 - | ebpf::JNE_REG32 - | ebpf::JSGT_IMM32 - | ebpf::JSGT_REG32 - | ebpf::JSGE_IMM32 - | ebpf::JSGE_REG32 - | ebpf::JSLT_IMM32 - | ebpf::JSLT_REG32 - | ebpf::JSLE_IMM32 - | ebpf::JSLE_REG32 - | ebpf::JSET_IMM32 - | ebpf::JSET_REG32 => { - let (fallthrough, target) = self.insn_targets[&(insn_ptr as u32)]; - - let is_reg = (insn.opc & BPF_X) != 0; - let is_32 = (insn.opc & BPF_JMP32) != 0; - let intcc = match insn.opc { - c if (c & BPF_ALU_OP_MASK) == BPF_JEQ => IntCC::Equal, - c if (c & BPF_ALU_OP_MASK) == BPF_JNE => IntCC::NotEqual, - c if (c & BPF_ALU_OP_MASK) == BPF_JGT => IntCC::UnsignedGreaterThan, - c if (c & BPF_ALU_OP_MASK) == BPF_JGE => IntCC::UnsignedGreaterThanOrEqual, - c if (c & BPF_ALU_OP_MASK) == BPF_JLT => IntCC::UnsignedLessThan, - c if (c & BPF_ALU_OP_MASK) == BPF_JLE => IntCC::UnsignedLessThanOrEqual, - c if (c & BPF_ALU_OP_MASK) == BPF_JSGT => IntCC::SignedGreaterThan, - c if (c & BPF_ALU_OP_MASK) == BPF_JSGE => IntCC::SignedGreaterThanOrEqual, - c if (c & BPF_ALU_OP_MASK) == BPF_JSLT => IntCC::SignedLessThan, - c if (c & BPF_ALU_OP_MASK) == BPF_JSLE => IntCC::SignedLessThanOrEqual, - // JSET is handled specially below - c if (c & BPF_ALU_OP_MASK) == BPF_JSET => IntCC::NotEqual, - _ => unreachable!(), - }; - - let lhs = if is_32 { - self.insn_dst32(bcx, &insn) - } else { - self.insn_dst(bcx, &insn) - }; - let rhs = match (is_reg, is_32) { - (true, false) => self.insn_src(bcx, &insn), - (true, true) => self.insn_src32(bcx, &insn), - (false, false) => self.insn_imm64(bcx, &insn), - (false, true) => self.insn_imm32(bcx, &insn), - }; - - let cmp_res = if (insn.opc & BPF_ALU_OP_MASK) == BPF_JSET { - bcx.ins().band(lhs, rhs) - } else { - bcx.ins().icmp(intcc, lhs, rhs) - }; - bcx.ins().brif(cmp_res, target, &[], fallthrough, &[]); - self.filled_blocks.insert(bcx.current_block().unwrap()); - } - - // Do not delegate the check to the verifier, since registered functions can be - // changed after the program has been verified. - ebpf::CALL => { - let func_ref = self - .helper_func_refs - .get(&(insn.imm as u32)) - .copied() - .ok_or_else(|| { - Error::new( - ErrorKind::Other, - format!( - "[CRANELIFT] Error: unknown helper function (id: {:#x})", - insn.imm as u32 - ), - ) - })?; - - let arg0 = bcx.use_var(self.registers[1]); - let arg1 = bcx.use_var(self.registers[2]); - let arg2 = bcx.use_var(self.registers[3]); - let arg3 = bcx.use_var(self.registers[4]); - let arg4 = bcx.use_var(self.registers[5]); - - let call = bcx.ins().call(func_ref, &[arg0, arg1, arg2, arg3, arg4]); - let ret = bcx.inst_results(call)[0]; - self.set_dst(bcx, &insn, ret); - } - ebpf::TAIL_CALL => unimplemented!(), - ebpf::EXIT => { - let ret = bcx.use_var(self.registers[0]); - bcx.ins().return_(&[ret]); - self.filled_blocks.insert(bcx.current_block().unwrap()); - } - _ => unimplemented!("inst: {:?}", insn), - } - - insn_ptr += 1; - } - - Ok(()) - } - - fn insn_imm64(&mut self, bcx: &mut FunctionBuilder, insn: &Insn) -> Value { - bcx.ins().iconst(I64, insn.imm as u64 as i64) - } - fn insn_imm32(&mut self, bcx: &mut FunctionBuilder, insn: &Insn) -> Value { - bcx.ins().iconst(I32, insn.imm as u32 as u64 as i64) - } - - fn insn_dst(&mut self, bcx: &mut FunctionBuilder, insn: &Insn) -> Value { - bcx.use_var(self.registers[insn.dst as usize]) - } - fn insn_dst32(&mut self, bcx: &mut FunctionBuilder, insn: &Insn) -> Value { - let dst = self.insn_dst(bcx, insn); - bcx.ins().ireduce(I32, dst) - } - - fn insn_src(&mut self, bcx: &mut FunctionBuilder, insn: &Insn) -> Value { - bcx.use_var(self.registers[insn.src as usize]) - } - fn insn_src32(&mut self, bcx: &mut FunctionBuilder, insn: &Insn) -> Value { - let src = self.insn_src(bcx, insn); - bcx.ins().ireduce(I32, src) - } - - fn set_dst(&mut self, bcx: &mut FunctionBuilder, insn: &Insn, val: Value) { - bcx.def_var(self.registers[insn.dst as usize], val); - } - fn set_dst32(&mut self, bcx: &mut FunctionBuilder, insn: &Insn, val: Value) { - let val32 = bcx.ins().uextend(I64, val); - self.set_dst(bcx, insn, val32); - } - - fn reg_load(&mut self, bcx: &mut FunctionBuilder, ty: Type, base: Value, offset: i16) -> Value { - self.insert_bounds_check(bcx, ty, base, offset); - - let mut flags = MemFlags::new(); - flags.set_endianness(Endianness::Little); - - bcx.ins().load(ty, flags, base, offset as i32) - } - fn reg_store( - &mut self, - bcx: &mut FunctionBuilder, - ty: Type, - base: Value, - offset: i16, - val: Value, - ) { - self.insert_bounds_check(bcx, ty, base, offset); - - let mut flags = MemFlags::new(); - flags.set_endianness(Endianness::Little); - - bcx.ins().store(flags, val, base, offset as i32); - } - - /// Inserts a bounds check for a memory access - /// - /// This emits a conditional trap if the access is out of bounds for any of the known - /// valid memory regions. These are the stack, the memory, and the mbuf. - fn insert_bounds_check( - &mut self, - bcx: &mut FunctionBuilder, - ty: Type, - base: Value, - offset: i16, - ) { - let access_size = bcx.ins().iconst(I64, ty.bytes() as i64); - - let offset = bcx.ins().iconst(I64, offset as i64); - let start_addr = bcx.ins().iadd(base, offset); - let end_addr = bcx.ins().iadd(start_addr, access_size); - - let does_not_overflow = - bcx.ins() - .icmp(IntCC::UnsignedGreaterThanOrEqual, end_addr, start_addr); - - // Check if it's a valid stack access - let stack_start = bcx.use_var(self.stack_start); - let stack_end = bcx.use_var(self.stack_end); - let stack_start_valid = - bcx.ins() - .icmp(IntCC::UnsignedGreaterThanOrEqual, start_addr, stack_start); - let stack_end_valid = bcx - .ins() - .icmp(IntCC::UnsignedLessThanOrEqual, end_addr, stack_end); - let stack_valid = bcx.ins().band(stack_start_valid, stack_end_valid); - - // Check if it's a valid memory access - let mem_start = bcx.use_var(self.mem_start); - let mem_end = bcx.use_var(self.mem_end); - let has_mem = bcx.ins().icmp_imm(IntCC::NotEqual, mem_start, 0); - let mem_start_valid = - bcx.ins() - .icmp(IntCC::UnsignedGreaterThanOrEqual, start_addr, mem_start); - let mem_end_valid = bcx - .ins() - .icmp(IntCC::UnsignedLessThanOrEqual, end_addr, mem_end); - - let mem_valid = bcx.ins().band(mem_start_valid, mem_end_valid); - let mem_valid = bcx.ins().band(mem_valid, has_mem); - - // Check if it's a valid mbuf access - let mbuf_start = bcx.use_var(self.mbuf_start); - let mbuf_end = bcx.use_var(self.mbuf_end); - let has_mbuf = bcx.ins().icmp_imm(IntCC::NotEqual, mbuf_start, 0); - let mbuf_start_valid = - bcx.ins() - .icmp(IntCC::UnsignedGreaterThanOrEqual, start_addr, mbuf_start); - let mbuf_end_valid = bcx - .ins() - .icmp(IntCC::UnsignedLessThanOrEqual, end_addr, mbuf_end); - let mbuf_valid = bcx.ins().band(mbuf_start_valid, mbuf_end_valid); - let mbuf_valid = bcx.ins().band(mbuf_valid, has_mbuf); - - // Join all of these checks together and trap if any of them fails - - // We need it to be valid to at least one region of memory - let valid_region = bcx.ins().bor(stack_valid, mem_valid); - let valid_region = bcx.ins().bor(valid_region, mbuf_valid); - - // And that it does not overflow - let valid = bcx.ins().band(does_not_overflow, valid_region); - - // TODO: We can potentially throw a custom trap code here to indicate - // which check failed. - bcx.ins().trapz(valid, TrapCode::HeapOutOfBounds); - } - - /// Analyze the program and build the CFG - /// - /// We do this because cranelift does not allow us to switch back to a previously - /// filled block and add instructions to it. So we can't split the program as we - /// translate it. - fn build_cfg(&mut self, bcx: &mut FunctionBuilder, prog: &[u8]) -> Result<(), Error> { - let mut insn_ptr: usize = 0; - while insn_ptr * ebpf::INSN_SIZE < prog.len() { - let insn = ebpf::get_insn(prog, insn_ptr); - - match insn.opc { - // This instruction consumes two opcodes - ebpf::LD_DW_IMM => { - insn_ptr += 1; - } - - ebpf::JA - | ebpf::JEQ_IMM - | ebpf::JEQ_REG - | ebpf::JGT_IMM - | ebpf::JGT_REG - | ebpf::JGE_IMM - | ebpf::JGE_REG - | ebpf::JLT_IMM - | ebpf::JLT_REG - | ebpf::JLE_IMM - | ebpf::JLE_REG - | ebpf::JNE_IMM - | ebpf::JNE_REG - | ebpf::JSGT_IMM - | ebpf::JSGT_REG - | ebpf::JSGE_IMM - | ebpf::JSGE_REG - | ebpf::JSLT_IMM - | ebpf::JSLT_REG - | ebpf::JSLE_IMM - | ebpf::JSLE_REG - | ebpf::JSET_IMM - | ebpf::JSET_REG - | ebpf::JEQ_IMM32 - | ebpf::JEQ_REG32 - | ebpf::JGT_IMM32 - | ebpf::JGT_REG32 - | ebpf::JGE_IMM32 - | ebpf::JGE_REG32 - | ebpf::JLT_IMM32 - | ebpf::JLT_REG32 - | ebpf::JLE_IMM32 - | ebpf::JLE_REG32 - | ebpf::JNE_IMM32 - | ebpf::JNE_REG32 - | ebpf::JSGT_IMM32 - | ebpf::JSGT_REG32 - | ebpf::JSGE_IMM32 - | ebpf::JSGE_REG32 - | ebpf::JSLT_IMM32 - | ebpf::JSLT_REG32 - | ebpf::JSLE_IMM32 - | ebpf::JSLE_REG32 - | ebpf::JSET_IMM32 - | ebpf::JSET_REG32 - | ebpf::EXIT - | ebpf::TAIL_CALL => { - self.prepare_jump_blocks(bcx, insn_ptr, &insn); - } - _ => {} - } - - insn_ptr += 1; - } - - Ok(()) - } - - fn prepare_jump_blocks(&mut self, bcx: &mut FunctionBuilder, insn_ptr: usize, insn: &Insn) { - let insn_ptr = insn_ptr as u32; - let next_pc: u32 = insn_ptr + 1; - let target_pc: u32 = (insn_ptr as isize + insn.off as isize + 1) - .try_into() - .unwrap(); - - // This is the fallthrough block - let fallthrough_block = *self - .insn_blocks - .entry(next_pc) - .or_insert_with(|| bcx.create_block()); - - // Jump Target - let target_block = *self - .insn_blocks - .entry(target_pc) - .or_insert_with(|| bcx.create_block()); - - // Mark the blocks for this instruction - self.insn_targets - .insert(insn_ptr, (fallthrough_block, target_block)); - } -} - -/// Contains the backing memory for a previously compiled function. -/// -/// Currently this will allways just contain code for a single function, but -/// in the future we might want to support multiple functions per module. -/// -/// Ensures that the backing memory is freed when dropped. -pub struct CraneliftProgram { - module: ManuallyDrop, - - main_id: FuncId, -} - -impl CraneliftProgram { - pub(crate) fn new(module: JITModule, main_id: FuncId) -> Self { - Self { - module: ManuallyDrop::new(module), - main_id, - } - } - - /// We shouldn't allow this function pointer to be exposed outside of this - /// module, since it's not guaranteed to be valid after the module is dropped. - pub(crate) fn get_main_function(&self) -> JittedFunction { - let function_ptr = self.module.get_finalized_function(self.main_id); - unsafe { mem::transmute(function_ptr) } - } - - /// Execute this module by calling the main function - pub fn execute( - &self, - mem_ptr: *mut u8, - mem_len: usize, - mbuff_ptr: *mut u8, - mbuff_len: usize, - ) -> u64 { - let main = self.get_main_function(); - - main(mem_ptr, mem_len, mbuff_ptr, mbuff_len) - } -} - -impl Drop for CraneliftProgram { - fn drop(&mut self) { - // We need to have an owned version of `JITModule` to be able to free - // it's memory. Use `ManuallyDrop` to get the owned `JITModule`. - // - // We can no longer use `module` after this, but since we are `Drop` - // it should be safe. - unsafe { - let module = ManuallyDrop::take(&mut self.module); - module.free_memory() - }; - } -} diff --git a/kernel/crates/rbpf/src/disassembler.rs b/kernel/crates/rbpf/src/disassembler.rs deleted file mode 100644 index 6d6572c5..00000000 --- a/kernel/crates/rbpf/src/disassembler.rs +++ /dev/null @@ -1,807 +0,0 @@ -// SPDX-License-Identifier: (Apache-2.0 OR MIT) -// Copyright 2017 6WIND S.A. - -//! Functions in this module are used to handle eBPF programs with a higher level representation, -//! for example to disassemble the code into a human-readable format. - -use alloc::{ - format, - string::{String, ToString}, - vec, - vec::Vec, -}; - -use log::warn; - -use crate::ebpf; - -#[inline] -fn alu_imm_str(name: &str, insn: &ebpf::Insn) -> String { - format!("{name} r{}, {:#x}", insn.dst, insn.imm) -} - -#[inline] -fn alu_reg_str(name: &str, insn: &ebpf::Insn) -> String { - format!("{name} r{}, r{}", insn.dst, insn.src) -} - -#[inline] -fn byteswap_str(name: &str, insn: &ebpf::Insn) -> String { - match insn.imm { - 16 | 32 | 64 => {} - _ => warn!("[Disassembler] Warning: Invalid offset value for {name} insn"), - } - format!("{name}{} r{}", insn.imm, insn.dst) -} - -#[inline] -fn ld_st_imm_str(name: &str, insn: &ebpf::Insn) -> String { - if insn.off >= 0 { - format!("{name} [r{}+{:#x}], {:#x}", insn.dst, insn.off, insn.imm) - } else { - format!( - "{name} [r{}-{:#x}], {:#x}", - insn.dst, - -(insn.off as isize), - insn.imm - ) - } -} - -#[inline] -fn ld_reg_str(name: &str, insn: &ebpf::Insn) -> String { - if insn.off >= 0 { - format!("{name} r{}, [r{}+{:#x}]", insn.dst, insn.src, insn.off) - } else { - format!( - "{name} r{}, [r{}-{:#x}]", - insn.dst, - insn.src, - -(insn.off as isize) - ) - } -} - -#[inline] -fn st_reg_str(name: &str, insn: &ebpf::Insn) -> String { - if insn.off >= 0 { - format!("{name} [r{}+{:#x}], r{}", insn.dst, insn.off, insn.src) - } else { - format!( - "{name} [r{}-{:#x}], r{}", - insn.dst, - -(insn.off as isize), - insn.src - ) - } -} - -#[inline] -fn ldabs_str(name: &str, insn: &ebpf::Insn) -> String { - format!("{name} {:#x}", insn.imm) -} - -#[inline] -fn ldind_str(name: &str, insn: &ebpf::Insn) -> String { - format!("{name} r{}, {:#x}", insn.src, insn.imm) -} - -#[inline] -fn jmp_imm_str(name: &str, insn: &ebpf::Insn) -> String { - if insn.off >= 0 { - format!("{name} r{}, {:#x}, +{:#x}", insn.dst, insn.imm, insn.off) - } else { - format!( - "{name} r{}, {:#x}, -{:#x}", - insn.dst, - insn.imm, - -(insn.off as isize) - ) - } -} - -#[inline] -fn jmp_reg_str(name: &str, insn: &ebpf::Insn) -> String { - if insn.off >= 0 { - format!("{name} r{}, r{}, +{:#x}", insn.dst, insn.src, insn.off) - } else { - format!( - "{name} r{}, r{}, -{:#x}", - insn.dst, - insn.src, - -(insn.off as isize) - ) - } -} - -/// High-level representation of an eBPF instruction. -/// -/// In addition to standard operation code and various operand, this struct has the following -/// properties: -/// -/// * It stores a name, corresponding to a mnemonic for the operation code. -/// * It also stores a description, which is a mnemonic for the full instruction, using the actual -/// values of the relevant operands, and that can be used for disassembling the eBPF program for -/// example. -/// * Immediate values are stored in an `i64` instead of a traditional i32, in order to merge the -/// two parts of (otherwise double-length) `LD_DW_IMM` instructions. -/// -/// See for the Linux kernel -/// documentation about eBPF, or for a -/// more concise version. -#[derive(Debug, PartialEq, Eq)] -pub struct HLInsn { - /// Operation code. - pub opc: u8, - /// Name (mnemonic). This name is not canon. - pub name: String, - /// Description of the instruction. This is not canon. - pub desc: String, - /// Destination register operand. - pub dst: u8, - /// Source register operand. - pub src: u8, - /// Offset operand. - pub off: i16, - /// Immediate value operand. For `LD_DW_IMM` instructions, contains the whole value merged from - /// the two 8-bytes parts of the instruction. - pub imm: i64, -} - -/// Return a vector of `struct HLInsn` built from an eBPF program. -/// -/// This is made public to provide a way to manipulate a program as a vector of instructions, in a -/// high-level format, for example for dumping the program instruction after instruction with a -/// custom format. -/// -/// Note that the two parts of `LD_DW_IMM` instructions (that have the size of two standard -/// instructions) are considered as making a single immediate value. As a consequence, the number -/// of instructions stored in the vector may not be equal to the size in bytes of the program -/// divided by the length of an instructions. -/// -/// To do so, the immediate value operand is stored as an `i64` instead as an i32, so be careful -/// when you use it (see example `examples/to_json.rs`). -/// -/// This is to oppose to `ebpf::to_insn_vec()` function, that treats instructions on a low-level -/// ground and do not merge the parts of `LD_DW_IMM`. Also, the version in `ebpf` module does not -/// use names or descriptions when storing the instructions. -/// -/// # Examples -/// -/// ``` -/// use rbpf::disassembler; -/// -/// let prog = &[ -/// 0x18, 0x00, 0x00, 0x00, 0x88, 0x77, 0x66, 0x55, -/// 0x00, 0x00, 0x00, 0x00, 0x44, 0x33, 0x22, 0x11, -/// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -/// ]; -/// -/// let v = disassembler::to_insn_vec(prog); -/// assert_eq!(v, vec![ -/// disassembler::HLInsn { -/// opc: 0x18, -/// name: "lddw".to_string(), -/// desc: "lddw r0, 0x1122334455667788".to_string(), -/// dst: 0, -/// src: 0, -/// off: 0, -/// imm: 0x1122334455667788 -/// }, -/// disassembler::HLInsn { -/// opc: 0x95, -/// name: "exit".to_string(), -/// desc: "exit".to_string(), -/// dst: 0, -/// src: 0, -/// off: 0, -/// imm: 0 -/// }, -/// ]); -/// ``` -pub fn to_insn_vec(prog: &[u8]) -> Vec { - if prog.len() % ebpf::INSN_SIZE != 0 { - panic!( - "[Disassembler] Error: eBPF program length must be a multiple of {:?} octets", - ebpf::INSN_SIZE - ); - } - if prog.is_empty() { - return vec![]; - } - - let mut res = vec![]; - let mut insn_ptr: usize = 0; - - while insn_ptr * ebpf::INSN_SIZE < prog.len() { - let insn = ebpf::get_insn(prog, insn_ptr); - - let name; - let desc; - let mut imm = insn.imm as i64; - match insn.opc { - // BPF_LD class - ebpf::LD_ABS_B => { - name = "ldabsb"; - desc = ldabs_str(name, &insn); - } - ebpf::LD_ABS_H => { - name = "ldabsh"; - desc = ldabs_str(name, &insn); - } - ebpf::LD_ABS_W => { - name = "ldabsw"; - desc = ldabs_str(name, &insn); - } - ebpf::LD_ABS_DW => { - name = "ldabsdw"; - desc = ldabs_str(name, &insn); - } - ebpf::LD_IND_B => { - name = "ldindb"; - desc = ldind_str(name, &insn); - } - ebpf::LD_IND_H => { - name = "ldindh"; - desc = ldind_str(name, &insn); - } - ebpf::LD_IND_W => { - name = "ldindw"; - desc = ldind_str(name, &insn); - } - ebpf::LD_IND_DW => { - name = "ldinddw"; - desc = ldind_str(name, &insn); - } - - ebpf::LD_DW_IMM => { - insn_ptr += 1; - let next_insn = ebpf::get_insn(prog, insn_ptr); - imm = ((insn.imm as u32) as u64 + ((next_insn.imm as u64) << 32)) as i64; - name = "lddw"; - desc = format!("{name} r{:}, {imm:#x}", insn.dst); - } - - // BPF_LDX class - ebpf::LD_B_REG => { - name = "ldxb"; - desc = ld_reg_str(name, &insn); - } - ebpf::LD_H_REG => { - name = "ldxh"; - desc = ld_reg_str(name, &insn); - } - ebpf::LD_W_REG => { - name = "ldxw"; - desc = ld_reg_str(name, &insn); - } - ebpf::LD_DW_REG => { - name = "ldxdw"; - desc = ld_reg_str(name, &insn); - } - - // BPF_ST class - ebpf::ST_B_IMM => { - name = "stb"; - desc = ld_st_imm_str(name, &insn); - } - ebpf::ST_H_IMM => { - name = "sth"; - desc = ld_st_imm_str(name, &insn); - } - ebpf::ST_W_IMM => { - name = "stw"; - desc = ld_st_imm_str(name, &insn); - } - ebpf::ST_DW_IMM => { - name = "stdw"; - desc = ld_st_imm_str(name, &insn); - } - - // BPF_STX class - ebpf::ST_B_REG => { - name = "stxb"; - desc = st_reg_str(name, &insn); - } - ebpf::ST_H_REG => { - name = "stxh"; - desc = st_reg_str(name, &insn); - } - ebpf::ST_W_REG => { - name = "stxw"; - desc = st_reg_str(name, &insn); - } - ebpf::ST_DW_REG => { - name = "stxdw"; - desc = st_reg_str(name, &insn); - } - ebpf::ST_W_XADD => { - name = "stxxaddw"; - desc = st_reg_str(name, &insn); - } - ebpf::ST_DW_XADD => { - name = "stxxadddw"; - desc = st_reg_str(name, &insn); - } - - // BPF_ALU class - ebpf::ADD32_IMM => { - name = "add32"; - desc = alu_imm_str(name, &insn); - } - ebpf::ADD32_REG => { - name = "add32"; - desc = alu_reg_str(name, &insn); - } - ebpf::SUB32_IMM => { - name = "sub32"; - desc = alu_imm_str(name, &insn); - } - ebpf::SUB32_REG => { - name = "sub32"; - desc = alu_reg_str(name, &insn); - } - ebpf::MUL32_IMM => { - name = "mul32"; - desc = alu_imm_str(name, &insn); - } - ebpf::MUL32_REG => { - name = "mul32"; - desc = alu_reg_str(name, &insn); - } - ebpf::DIV32_IMM => { - name = "div32"; - desc = alu_imm_str(name, &insn); - } - ebpf::DIV32_REG => { - name = "div32"; - desc = alu_reg_str(name, &insn); - } - ebpf::OR32_IMM => { - name = "or32"; - desc = alu_imm_str(name, &insn); - } - ebpf::OR32_REG => { - name = "or32"; - desc = alu_reg_str(name, &insn); - } - ebpf::AND32_IMM => { - name = "and32"; - desc = alu_imm_str(name, &insn); - } - ebpf::AND32_REG => { - name = "and32"; - desc = alu_reg_str(name, &insn); - } - ebpf::LSH32_IMM => { - name = "lsh32"; - desc = alu_imm_str(name, &insn); - } - ebpf::LSH32_REG => { - name = "lsh32"; - desc = alu_reg_str(name, &insn); - } - ebpf::RSH32_IMM => { - name = "rsh32"; - desc = alu_imm_str(name, &insn); - } - ebpf::RSH32_REG => { - name = "rsh32"; - desc = alu_reg_str(name, &insn); - } - ebpf::NEG32 => { - name = "neg32"; - desc = format!("{name} r{:}", insn.dst); - } - ebpf::MOD32_IMM => { - name = "mod32"; - desc = alu_imm_str(name, &insn); - } - ebpf::MOD32_REG => { - name = "mod32"; - desc = alu_reg_str(name, &insn); - } - ebpf::XOR32_IMM => { - name = "xor32"; - desc = alu_imm_str(name, &insn); - } - ebpf::XOR32_REG => { - name = "xor32"; - desc = alu_reg_str(name, &insn); - } - ebpf::MOV32_IMM => { - name = "mov32"; - desc = alu_imm_str(name, &insn); - } - ebpf::MOV32_REG => { - name = "mov32"; - desc = alu_reg_str(name, &insn); - } - ebpf::ARSH32_IMM => { - name = "arsh32"; - desc = alu_imm_str(name, &insn); - } - ebpf::ARSH32_REG => { - name = "arsh32"; - desc = alu_reg_str(name, &insn); - } - ebpf::LE => { - name = "le"; - desc = byteswap_str(name, &insn); - } - ebpf::BE => { - name = "be"; - desc = byteswap_str(name, &insn); - } - - // BPF_ALU64 class - ebpf::ADD64_IMM => { - name = "add64"; - desc = alu_imm_str(name, &insn); - } - ebpf::ADD64_REG => { - name = "add64"; - desc = alu_reg_str(name, &insn); - } - ebpf::SUB64_IMM => { - name = "sub64"; - desc = alu_imm_str(name, &insn); - } - ebpf::SUB64_REG => { - name = "sub64"; - desc = alu_reg_str(name, &insn); - } - ebpf::MUL64_IMM => { - name = "mul64"; - desc = alu_imm_str(name, &insn); - } - ebpf::MUL64_REG => { - name = "mul64"; - desc = alu_reg_str(name, &insn); - } - ebpf::DIV64_IMM => { - name = "div64"; - desc = alu_imm_str(name, &insn); - } - ebpf::DIV64_REG => { - name = "div64"; - desc = alu_reg_str(name, &insn); - } - ebpf::OR64_IMM => { - name = "or64"; - desc = alu_imm_str(name, &insn); - } - ebpf::OR64_REG => { - name = "or64"; - desc = alu_reg_str(name, &insn); - } - ebpf::AND64_IMM => { - name = "and64"; - desc = alu_imm_str(name, &insn); - } - ebpf::AND64_REG => { - name = "and64"; - desc = alu_reg_str(name, &insn); - } - ebpf::LSH64_IMM => { - name = "lsh64"; - desc = alu_imm_str(name, &insn); - } - ebpf::LSH64_REG => { - name = "lsh64"; - desc = alu_reg_str(name, &insn); - } - ebpf::RSH64_IMM => { - name = "rsh64"; - desc = alu_imm_str(name, &insn); - } - ebpf::RSH64_REG => { - name = "rsh64"; - desc = alu_reg_str(name, &insn); - } - ebpf::NEG64 => { - name = "neg64"; - desc = format!("{name} r{:}", insn.dst); - } - ebpf::MOD64_IMM => { - name = "mod64"; - desc = alu_imm_str(name, &insn); - } - ebpf::MOD64_REG => { - name = "mod64"; - desc = alu_reg_str(name, &insn); - } - ebpf::XOR64_IMM => { - name = "xor64"; - desc = alu_imm_str(name, &insn); - } - ebpf::XOR64_REG => { - name = "xor64"; - desc = alu_reg_str(name, &insn); - } - ebpf::MOV64_IMM => { - name = "mov64"; - desc = alu_imm_str(name, &insn); - } - ebpf::MOV64_REG => { - name = "mov64"; - desc = alu_reg_str(name, &insn); - } - ebpf::ARSH64_IMM => { - name = "arsh64"; - desc = alu_imm_str(name, &insn); - } - ebpf::ARSH64_REG => { - name = "arsh64"; - desc = alu_reg_str(name, &insn); - } - - // BPF_JMP class - ebpf::JA => { - name = "ja"; - desc = if insn.off >= 0 { - format!("{name} +{:#x}", insn.off) - } else { - format!("{name} -{:#x}", -insn.off) - } - } - ebpf::JEQ_IMM => { - name = "jeq"; - desc = jmp_imm_str(name, &insn); - } - ebpf::JEQ_REG => { - name = "jeq"; - desc = jmp_reg_str(name, &insn); - } - ebpf::JGT_IMM => { - name = "jgt"; - desc = jmp_imm_str(name, &insn); - } - ebpf::JGT_REG => { - name = "jgt"; - desc = jmp_reg_str(name, &insn); - } - ebpf::JGE_IMM => { - name = "jge"; - desc = jmp_imm_str(name, &insn); - } - ebpf::JGE_REG => { - name = "jge"; - desc = jmp_reg_str(name, &insn); - } - ebpf::JLT_IMM => { - name = "jlt"; - desc = jmp_imm_str(name, &insn); - } - ebpf::JLT_REG => { - name = "jlt"; - desc = jmp_reg_str(name, &insn); - } - ebpf::JLE_IMM => { - name = "jle"; - desc = jmp_imm_str(name, &insn); - } - ebpf::JLE_REG => { - name = "jle"; - desc = jmp_reg_str(name, &insn); - } - ebpf::JSET_IMM => { - name = "jset"; - desc = jmp_imm_str(name, &insn); - } - ebpf::JSET_REG => { - name = "jset"; - desc = jmp_reg_str(name, &insn); - } - ebpf::JNE_IMM => { - name = "jne"; - desc = jmp_imm_str(name, &insn); - } - ebpf::JNE_REG => { - name = "jne"; - desc = jmp_reg_str(name, &insn); - } - ebpf::JSGT_IMM => { - name = "jsgt"; - desc = jmp_imm_str(name, &insn); - } - ebpf::JSGT_REG => { - name = "jsgt"; - desc = jmp_reg_str(name, &insn); - } - ebpf::JSGE_IMM => { - name = "jsge"; - desc = jmp_imm_str(name, &insn); - } - ebpf::JSGE_REG => { - name = "jsge"; - desc = jmp_reg_str(name, &insn); - } - ebpf::JSLT_IMM => { - name = "jslt"; - desc = jmp_imm_str(name, &insn); - } - ebpf::JSLT_REG => { - name = "jslt"; - desc = jmp_reg_str(name, &insn); - } - ebpf::JSLE_IMM => { - name = "jsle"; - desc = jmp_imm_str(name, &insn); - } - ebpf::JSLE_REG => { - name = "jsle"; - desc = jmp_reg_str(name, &insn); - } - ebpf::CALL => { - name = "call"; - desc = format!("{name} {:#x}", insn.imm); - } - ebpf::TAIL_CALL => { - name = "tail_call"; - desc = name.to_string(); - } - ebpf::EXIT => { - name = "exit"; - desc = name.to_string(); - } - - // BPF_JMP32 class - ebpf::JEQ_IMM32 => { - name = "jeq32"; - desc = jmp_imm_str(name, &insn); - } - ebpf::JEQ_REG32 => { - name = "jeq32"; - desc = jmp_reg_str(name, &insn); - } - ebpf::JGT_IMM32 => { - name = "jgt32"; - desc = jmp_imm_str(name, &insn); - } - ebpf::JGT_REG32 => { - name = "jgt32"; - desc = jmp_reg_str(name, &insn); - } - ebpf::JGE_IMM32 => { - name = "jge32"; - desc = jmp_imm_str(name, &insn); - } - ebpf::JGE_REG32 => { - name = "jge32"; - desc = jmp_reg_str(name, &insn); - } - ebpf::JLT_IMM32 => { - name = "jlt32"; - desc = jmp_imm_str(name, &insn); - } - ebpf::JLT_REG32 => { - name = "jlt32"; - desc = jmp_reg_str(name, &insn); - } - ebpf::JLE_IMM32 => { - name = "jle32"; - desc = jmp_imm_str(name, &insn); - } - ebpf::JLE_REG32 => { - name = "jle32"; - desc = jmp_reg_str(name, &insn); - } - ebpf::JSET_IMM32 => { - name = "jset32"; - desc = jmp_imm_str(name, &insn); - } - ebpf::JSET_REG32 => { - name = "jset32"; - desc = jmp_reg_str(name, &insn); - } - ebpf::JNE_IMM32 => { - name = "jne32"; - desc = jmp_imm_str(name, &insn); - } - ebpf::JNE_REG32 => { - name = "jne32"; - desc = jmp_reg_str(name, &insn); - } - ebpf::JSGT_IMM32 => { - name = "jsgt32"; - desc = jmp_imm_str(name, &insn); - } - ebpf::JSGT_REG32 => { - name = "jsgt32"; - desc = jmp_reg_str(name, &insn); - } - ebpf::JSGE_IMM32 => { - name = "jsge32"; - desc = jmp_imm_str(name, &insn); - } - ebpf::JSGE_REG32 => { - name = "jsge32"; - desc = jmp_reg_str(name, &insn); - } - ebpf::JSLT_IMM32 => { - name = "jslt32"; - desc = jmp_imm_str(name, &insn); - } - ebpf::JSLT_REG32 => { - name = "jslt32"; - desc = jmp_reg_str(name, &insn); - } - ebpf::JSLE_IMM32 => { - name = "jsle32"; - desc = jmp_imm_str(name, &insn); - } - ebpf::JSLE_REG32 => { - name = "jsle32"; - desc = jmp_reg_str(name, &insn); - } - - _ => { - panic!( - "[Disassembler] Error: unknown eBPF opcode {:#2x} (insn #{:?})", - insn.opc, insn_ptr - ); - } - }; - - let hl_insn = HLInsn { - opc: insn.opc, - name: name.to_string(), - desc, - dst: insn.dst, - src: insn.src, - off: insn.off, - imm, - }; - - res.push(hl_insn); - - insn_ptr += 1; - } - res -} - -/// Disassemble an eBPF program into human-readable instructions and prints it to standard output. -/// -/// The program is not checked for errors or inconsistencies. -/// -/// # Examples -/// -/// ``` -/// use rbpf::disassembler; -/// let prog = &[ -/// 0x07, 0x01, 0x00, 0x00, 0x05, 0x06, 0x00, 0x00, -/// 0xb7, 0x02, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, -/// 0xbf, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/// 0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, -/// 0x87, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -/// ]; -/// disassembler::disassemble(prog); -/// # // "\nadd64 r1, 0x605\nmov64 r2, 0x32\nmov64 r1, r0\nbe16 r0\nneg64 r2\nexit" -/// ``` -/// -/// This will produce the following output: -/// -/// ```test -/// add64 r1, 0x605 -/// mov64 r2, 0x32 -/// mov64 r1, r0 -/// be16 r0 -/// neg64 r2 -/// exit -/// ``` -pub fn disassemble(prog: &[u8]) { - #[cfg(feature = "std")] - { - for insn in to_insn_vec(prog) { - println!("{}", insn.desc); - } - } - #[cfg(not(feature = "std"))] - { - for insn in to_insn_vec(prog) { - log::info!("{}", insn.desc); - } - } -} diff --git a/kernel/crates/rbpf/src/ebpf.rs b/kernel/crates/rbpf/src/ebpf.rs deleted file mode 100644 index 33018b06..00000000 --- a/kernel/crates/rbpf/src/ebpf.rs +++ /dev/null @@ -1,635 +0,0 @@ -// SPDX-License-Identifier: (Apache-2.0 OR MIT) -// Copyright 2016 6WIND S.A. - -//! This module contains all the definitions related to eBPF, and some functions permitting to -//! manipulate eBPF instructions. -//! -//! The number of bytes in an instruction, the maximum number of instructions in a program, and -//! also all operation codes are defined here as constants. -//! -//! The structure for an instruction used by this crate, as well as the function to extract it from -//! a program, is also defined in the module. -//! -//! To learn more about these instructions, see the Linux kernel documentation: -//! , or for a shorter version of -//! the list of the operation codes: - -use alloc::{vec, vec::Vec}; - -use byteorder::{ByteOrder, LittleEndian}; - -/// The maximum call depth is 8 -pub const RBPF_MAX_CALL_DEPTH: usize = 8; - -/// Maximum number of instructions in an eBPF program. -pub const PROG_MAX_INSNS: usize = 1000000; -/// Size of an eBPF instructions, in bytes. -pub const INSN_SIZE: usize = 8; -/// Maximum size of an eBPF program, in bytes. -pub const PROG_MAX_SIZE: usize = PROG_MAX_INSNS * INSN_SIZE; -/// Stack for the eBPF stack, in bytes. -pub const STACK_SIZE: usize = 512; - -// eBPF op codes. -// See also https://www.kernel.org/doc/Documentation/networking/filter.txt - -// Three least significant bits are operation class: -/// BPF operation class: load from immediate. -pub const BPF_LD: u8 = 0x00; -/// BPF operation class: load from register. -pub const BPF_LDX: u8 = 0x01; -/// BPF operation class: store immediate. -pub const BPF_ST: u8 = 0x02; -/// BPF operation class: store value from register. -pub const BPF_STX: u8 = 0x03; -/// BPF operation class: 32 bits arithmetic operation. -pub const BPF_ALU: u8 = 0x04; -/// BPF operation class: jump (64-bit wide operands for comparisons). -pub const BPF_JMP: u8 = 0x05; -/// BPF operation class: jump (32-bit wide operands for comparisons). -pub const BPF_JMP32: u8 = 0x06; -// [ class 6 unused, reserved for future use ] -/// BPF operation class: 64 bits arithmetic operation. -pub const BPF_ALU64: u8 = 0x07; - -// For load and store instructions: -// +------------+--------+------------+ -// | 3 bits | 2 bits | 3 bits | -// | mode | size | insn class | -// +------------+--------+------------+ -// (MSB) (LSB) - -// Size modifiers: -/// BPF size modifier: word (4 bytes). -pub const BPF_W: u8 = 0x00; -/// BPF size modifier: half-word (2 bytes). -pub const BPF_H: u8 = 0x08; -/// BPF size modifier: byte (1 byte). -pub const BPF_B: u8 = 0x10; -/// BPF size modifier: double word (8 bytes). -pub const BPF_DW: u8 = 0x18; - -// Mode modifiers: -/// BPF mode modifier: immediate value. -pub const BPF_IMM: u8 = 0x00; -/// BPF mode modifier: absolute load. -pub const BPF_ABS: u8 = 0x20; -/// BPF mode modifier: indirect load. -pub const BPF_IND: u8 = 0x40; -/// BPF mode modifier: load from / store to memory. -pub const BPF_MEM: u8 = 0x60; -// [ 0x80 reserved ] -// [ 0xa0 reserved ] -/// BPF mode modifier: exclusive add. -pub const BPF_XADD: u8 = 0xc0; - -// For arithmetic (BPF_ALU/BPF_ALU64) and jump (BPF_JMP) instructions: -// +----------------+--------+--------+ -// | 4 bits |1 b.| 3 bits | -// | operation code | src| insn class | -// +----------------+----+------------+ -// (MSB) (LSB) - -// Source modifiers: -/// BPF source operand modifier: 32-bit immediate value. -pub const BPF_K: u8 = 0x00; -/// BPF source operand modifier: `src` register. -pub const BPF_X: u8 = 0x08; - -// Operation codes -- BPF_ALU or BPF_ALU64 classes: -/// BPF ALU/ALU64 operation code: addition. -pub const BPF_ADD: u8 = 0x00; -/// BPF ALU/ALU64 operation code: subtraction. -pub const BPF_SUB: u8 = 0x10; -/// BPF ALU/ALU64 operation code: multiplication. -pub const BPF_MUL: u8 = 0x20; -/// BPF ALU/ALU64 operation code: division. -pub const BPF_DIV: u8 = 0x30; -/// BPF ALU/ALU64 operation code: or. -pub const BPF_OR: u8 = 0x40; -/// BPF ALU/ALU64 operation code: and. -pub const BPF_AND: u8 = 0x50; -/// BPF ALU/ALU64 operation code: left shift. -pub const BPF_LSH: u8 = 0x60; -/// BPF ALU/ALU64 operation code: right shift. -pub const BPF_RSH: u8 = 0x70; -/// BPF ALU/ALU64 operation code: negation. -pub const BPF_NEG: u8 = 0x80; -/// BPF ALU/ALU64 operation code: modulus. -pub const BPF_MOD: u8 = 0x90; -/// BPF ALU/ALU64 operation code: exclusive or. -pub const BPF_XOR: u8 = 0xa0; -/// BPF ALU/ALU64 operation code: move. -pub const BPF_MOV: u8 = 0xb0; -/// BPF ALU/ALU64 operation code: sign extending right shift. -pub const BPF_ARSH: u8 = 0xc0; -/// BPF ALU/ALU64 operation code: endianness conversion. -pub const BPF_END: u8 = 0xd0; - -// Operation codes -- BPF_JMP or BPF_JMP32 classes: -/// BPF JMP operation code: jump. -pub const BPF_JA: u8 = 0x00; -/// BPF JMP operation code: jump if equal. -pub const BPF_JEQ: u8 = 0x10; -/// BPF JMP operation code: jump if greater than. -pub const BPF_JGT: u8 = 0x20; -/// BPF JMP operation code: jump if greater or equal. -pub const BPF_JGE: u8 = 0x30; -/// BPF JMP operation code: jump if `src` & `reg`. -pub const BPF_JSET: u8 = 0x40; -/// BPF JMP operation code: jump if not equal. -pub const BPF_JNE: u8 = 0x50; -/// BPF JMP operation code: jump if greater than (signed). -pub const BPF_JSGT: u8 = 0x60; -/// BPF JMP operation code: jump if greater or equal (signed). -pub const BPF_JSGE: u8 = 0x70; -/// BPF JMP operation code: helper function call. -pub const BPF_CALL: u8 = 0x80; -/// BPF JMP operation code: return from program. -pub const BPF_EXIT: u8 = 0x90; -/// BPF JMP operation code: jump if lower than. -pub const BPF_JLT: u8 = 0xa0; -/// BPF JMP operation code: jump if lower or equal. -pub const BPF_JLE: u8 = 0xb0; -/// BPF JMP operation code: jump if lower than (signed). -pub const BPF_JSLT: u8 = 0xc0; -/// BPF JMP operation code: jump if lower or equal (signed). -pub const BPF_JSLE: u8 = 0xd0; - -// Op codes -// (Following operation names are not “official”, but may be proper to rbpf; Linux kernel only -// combines above flags and does not attribute a name per operation.) - -/// BPF opcode: `ldabsb src, dst, imm`. -pub const LD_ABS_B: u8 = BPF_LD | BPF_ABS | BPF_B; -/// BPF opcode: `ldabsh src, dst, imm`. -pub const LD_ABS_H: u8 = BPF_LD | BPF_ABS | BPF_H; -/// BPF opcode: `ldabsw src, dst, imm`. -pub const LD_ABS_W: u8 = BPF_LD | BPF_ABS | BPF_W; -/// BPF opcode: `ldabsdw src, dst, imm`. -pub const LD_ABS_DW: u8 = BPF_LD | BPF_ABS | BPF_DW; -/// BPF opcode: `ldindb src, dst, imm`. -pub const LD_IND_B: u8 = BPF_LD | BPF_IND | BPF_B; -/// BPF opcode: `ldindh src, dst, imm`. -pub const LD_IND_H: u8 = BPF_LD | BPF_IND | BPF_H; -/// BPF opcode: `ldindw src, dst, imm`. -pub const LD_IND_W: u8 = BPF_LD | BPF_IND | BPF_W; -/// BPF opcode: `ldinddw src, dst, imm`. -pub const LD_IND_DW: u8 = BPF_LD | BPF_IND | BPF_DW; - -#[allow(unknown_lints)] -#[allow(clippy::eq_op)] -/// BPF opcode: `lddw dst, imm` /// `dst = imm`. -pub const LD_DW_IMM: u8 = BPF_LD | BPF_IMM | BPF_DW; -/// BPF opcode: `ldxb dst, [src + off]` /// `dst = (src + off) as u8`. -pub const LD_B_REG: u8 = BPF_LDX | BPF_MEM | BPF_B; -/// BPF opcode: `ldxh dst, [src + off]` /// `dst = (src + off) as u16`. -pub const LD_H_REG: u8 = BPF_LDX | BPF_MEM | BPF_H; -/// BPF opcode: `ldxw dst, [src + off]` /// `dst = (src + off) as u32`. -pub const LD_W_REG: u8 = BPF_LDX | BPF_MEM | BPF_W; -/// BPF opcode: `ldxdw dst, [src + off]` /// `dst = (src + off) as u64`. -pub const LD_DW_REG: u8 = BPF_LDX | BPF_MEM | BPF_DW; -/// BPF opcode: `stb [dst + off], imm` /// `(dst + offset) as u8 = imm`. -pub const ST_B_IMM: u8 = BPF_ST | BPF_MEM | BPF_B; -/// BPF opcode: `sth [dst + off], imm` /// `(dst + offset) as u16 = imm`. -pub const ST_H_IMM: u8 = BPF_ST | BPF_MEM | BPF_H; -/// BPF opcode: `stw [dst + off], imm` /// `(dst + offset) as u32 = imm`. -pub const ST_W_IMM: u8 = BPF_ST | BPF_MEM | BPF_W; -/// BPF opcode: `stdw [dst + off], imm` /// `(dst + offset) as u64 = imm`. -pub const ST_DW_IMM: u8 = BPF_ST | BPF_MEM | BPF_DW; -/// BPF opcode: `stxb [dst + off], src` /// `(dst + offset) as u8 = src`. -pub const ST_B_REG: u8 = BPF_STX | BPF_MEM | BPF_B; -/// BPF opcode: `stxh [dst + off], src` /// `(dst + offset) as u16 = src`. -pub const ST_H_REG: u8 = BPF_STX | BPF_MEM | BPF_H; -/// BPF opcode: `stxw [dst + off], src` /// `(dst + offset) as u32 = src`. -pub const ST_W_REG: u8 = BPF_STX | BPF_MEM | BPF_W; -/// BPF opcode: `stxdw [dst + off], src` /// `(dst + offset) as u64 = src`. -pub const ST_DW_REG: u8 = BPF_STX | BPF_MEM | BPF_DW; - -/// BPF opcode: `stxxaddw [dst + off], src`. -pub const ST_W_XADD: u8 = BPF_STX | BPF_XADD | BPF_W; -/// BPF opcode: `stxxadddw [dst + off], src`. -pub const ST_DW_XADD: u8 = BPF_STX | BPF_XADD | BPF_DW; - -/// BPF opcode: `add32 dst, imm` /// `dst += imm`. -pub const ADD32_IMM: u8 = BPF_ALU | BPF_K | BPF_ADD; -/// BPF opcode: `add32 dst, src` /// `dst += src`. -pub const ADD32_REG: u8 = BPF_ALU | BPF_X | BPF_ADD; -/// BPF opcode: `sub32 dst, imm` /// `dst -= imm`. -pub const SUB32_IMM: u8 = BPF_ALU | BPF_K | BPF_SUB; -/// BPF opcode: `sub32 dst, src` /// `dst -= src`. -pub const SUB32_REG: u8 = BPF_ALU | BPF_X | BPF_SUB; -/// BPF opcode: `mul32 dst, imm` /// `dst *= imm`. -pub const MUL32_IMM: u8 = BPF_ALU | BPF_K | BPF_MUL; -/// BPF opcode: `mul32 dst, src` /// `dst *= src`. -pub const MUL32_REG: u8 = BPF_ALU | BPF_X | BPF_MUL; -/// BPF opcode: `div32 dst, imm` /// `dst /= imm`. -pub const DIV32_IMM: u8 = BPF_ALU | BPF_K | BPF_DIV; -/// BPF opcode: `div32 dst, src` /// `dst /= src`. -pub const DIV32_REG: u8 = BPF_ALU | BPF_X | BPF_DIV; -/// BPF opcode: `or32 dst, imm` /// `dst |= imm`. -pub const OR32_IMM: u8 = BPF_ALU | BPF_K | BPF_OR; -/// BPF opcode: `or32 dst, src` /// `dst |= src`. -pub const OR32_REG: u8 = BPF_ALU | BPF_X | BPF_OR; -/// BPF opcode: `and32 dst, imm` /// `dst &= imm`. -pub const AND32_IMM: u8 = BPF_ALU | BPF_K | BPF_AND; -/// BPF opcode: `and32 dst, src` /// `dst &= src`. -pub const AND32_REG: u8 = BPF_ALU | BPF_X | BPF_AND; -/// BPF opcode: `lsh32 dst, imm` /// `dst <<= imm`. -pub const LSH32_IMM: u8 = BPF_ALU | BPF_K | BPF_LSH; -/// BPF opcode: `lsh32 dst, src` /// `dst <<= src`. -pub const LSH32_REG: u8 = BPF_ALU | BPF_X | BPF_LSH; -/// BPF opcode: `rsh32 dst, imm` /// `dst >>= imm`. -pub const RSH32_IMM: u8 = BPF_ALU | BPF_K | BPF_RSH; -/// BPF opcode: `rsh32 dst, src` /// `dst >>= src`. -pub const RSH32_REG: u8 = BPF_ALU | BPF_X | BPF_RSH; -/// BPF opcode: `neg32 dst` /// `dst = -dst`. -pub const NEG32: u8 = BPF_ALU | BPF_NEG; -/// BPF opcode: `mod32 dst, imm` /// `dst %= imm`. -pub const MOD32_IMM: u8 = BPF_ALU | BPF_K | BPF_MOD; -/// BPF opcode: `mod32 dst, src` /// `dst %= src`. -pub const MOD32_REG: u8 = BPF_ALU | BPF_X | BPF_MOD; -/// BPF opcode: `xor32 dst, imm` /// `dst ^= imm`. -pub const XOR32_IMM: u8 = BPF_ALU | BPF_K | BPF_XOR; -/// BPF opcode: `xor32 dst, src` /// `dst ^= src`. -pub const XOR32_REG: u8 = BPF_ALU | BPF_X | BPF_XOR; -/// BPF opcode: `mov32 dst, imm` /// `dst = imm`. -pub const MOV32_IMM: u8 = BPF_ALU | BPF_K | BPF_MOV; -/// BPF opcode: `mov32 dst, src` /// `dst = src`. -pub const MOV32_REG: u8 = BPF_ALU | BPF_X | BPF_MOV; -/// BPF opcode: `arsh32 dst, imm` /// `dst >>= imm (arithmetic)`. -/// -/// -pub const ARSH32_IMM: u8 = BPF_ALU | BPF_K | BPF_ARSH; -/// BPF opcode: `arsh32 dst, src` /// `dst >>= src (arithmetic)`. -/// -/// -pub const ARSH32_REG: u8 = BPF_ALU | BPF_X | BPF_ARSH; - -/// BPF opcode: `le dst` /// `dst = htole(dst), with imm in {16, 32, 64}`. -pub const LE: u8 = BPF_ALU | BPF_K | BPF_END; -/// BPF opcode: `be dst` /// `dst = htobe(dst), with imm in {16, 32, 64}`. -pub const BE: u8 = BPF_ALU | BPF_X | BPF_END; - -/// BPF opcode: `add64 dst, imm` /// `dst += imm`. -pub const ADD64_IMM: u8 = BPF_ALU64 | BPF_K | BPF_ADD; -/// BPF opcode: `add64 dst, src` /// `dst += src`. -pub const ADD64_REG: u8 = BPF_ALU64 | BPF_X | BPF_ADD; -/// BPF opcode: `sub64 dst, imm` /// `dst -= imm`. -pub const SUB64_IMM: u8 = BPF_ALU64 | BPF_K | BPF_SUB; -/// BPF opcode: `sub64 dst, src` /// `dst -= src`. -pub const SUB64_REG: u8 = BPF_ALU64 | BPF_X | BPF_SUB; -/// BPF opcode: `div64 dst, imm` /// `dst /= imm`. -pub const MUL64_IMM: u8 = BPF_ALU64 | BPF_K | BPF_MUL; -/// BPF opcode: `div64 dst, src` /// `dst /= src`. -pub const MUL64_REG: u8 = BPF_ALU64 | BPF_X | BPF_MUL; -/// BPF opcode: `div64 dst, imm` /// `dst /= imm`. -pub const DIV64_IMM: u8 = BPF_ALU64 | BPF_K | BPF_DIV; -/// BPF opcode: `div64 dst, src` /// `dst /= src`. -pub const DIV64_REG: u8 = BPF_ALU64 | BPF_X | BPF_DIV; -/// BPF opcode: `or64 dst, imm` /// `dst |= imm`. -pub const OR64_IMM: u8 = BPF_ALU64 | BPF_K | BPF_OR; -/// BPF opcode: `or64 dst, src` /// `dst |= src`. -pub const OR64_REG: u8 = BPF_ALU64 | BPF_X | BPF_OR; -/// BPF opcode: `and64 dst, imm` /// `dst &= imm`. -pub const AND64_IMM: u8 = BPF_ALU64 | BPF_K | BPF_AND; -/// BPF opcode: `and64 dst, src` /// `dst &= src`. -pub const AND64_REG: u8 = BPF_ALU64 | BPF_X | BPF_AND; -/// BPF opcode: `lsh64 dst, imm` /// `dst <<= imm`. -pub const LSH64_IMM: u8 = BPF_ALU64 | BPF_K | BPF_LSH; -/// BPF opcode: `lsh64 dst, src` /// `dst <<= src`. -pub const LSH64_REG: u8 = BPF_ALU64 | BPF_X | BPF_LSH; -/// BPF opcode: `rsh64 dst, imm` /// `dst >>= imm`. -pub const RSH64_IMM: u8 = BPF_ALU64 | BPF_K | BPF_RSH; -/// BPF opcode: `rsh64 dst, src` /// `dst >>= src`. -pub const RSH64_REG: u8 = BPF_ALU64 | BPF_X | BPF_RSH; -/// BPF opcode: `neg64 dst, imm` /// `dst = -dst`. -pub const NEG64: u8 = BPF_ALU64 | BPF_NEG; -/// BPF opcode: `mod64 dst, imm` /// `dst %= imm`. -pub const MOD64_IMM: u8 = BPF_ALU64 | BPF_K | BPF_MOD; -/// BPF opcode: `mod64 dst, src` /// `dst %= src`. -pub const MOD64_REG: u8 = BPF_ALU64 | BPF_X | BPF_MOD; -/// BPF opcode: `xor64 dst, imm` /// `dst ^= imm`. -pub const XOR64_IMM: u8 = BPF_ALU64 | BPF_K | BPF_XOR; -/// BPF opcode: `xor64 dst, src` /// `dst ^= src`. -pub const XOR64_REG: u8 = BPF_ALU64 | BPF_X | BPF_XOR; -/// BPF opcode: `mov64 dst, imm` /// `dst = imm`. -pub const MOV64_IMM: u8 = BPF_ALU64 | BPF_K | BPF_MOV; -/// BPF opcode: `mov64 dst, src` /// `dst = src`. -pub const MOV64_REG: u8 = BPF_ALU64 | BPF_X | BPF_MOV; -/// BPF opcode: `arsh64 dst, imm` /// `dst >>= imm (arithmetic)`. -/// -/// -pub const ARSH64_IMM: u8 = BPF_ALU64 | BPF_K | BPF_ARSH; -/// BPF opcode: `arsh64 dst, src` /// `dst >>= src (arithmetic)`. -/// -/// -pub const ARSH64_REG: u8 = BPF_ALU64 | BPF_X | BPF_ARSH; - -/// BPF opcode: `ja +off` /// `PC += off`. -pub const JA: u8 = BPF_JMP | BPF_JA; -/// BPF opcode: `jeq dst, imm, +off` /// `PC += off if dst == imm`. -pub const JEQ_IMM: u8 = BPF_JMP | BPF_K | BPF_JEQ; -/// BPF opcode: `jeq dst, src, +off` /// `PC += off if dst == src`. -pub const JEQ_REG: u8 = BPF_JMP | BPF_X | BPF_JEQ; -/// BPF opcode: `jgt dst, imm, +off` /// `PC += off if dst > imm`. -pub const JGT_IMM: u8 = BPF_JMP | BPF_K | BPF_JGT; -/// BPF opcode: `jgt dst, src, +off` /// `PC += off if dst > src`. -pub const JGT_REG: u8 = BPF_JMP | BPF_X | BPF_JGT; -/// BPF opcode: `jge dst, imm, +off` /// `PC += off if dst >= imm`. -pub const JGE_IMM: u8 = BPF_JMP | BPF_K | BPF_JGE; -/// BPF opcode: `jge dst, src, +off` /// `PC += off if dst >= src`. -pub const JGE_REG: u8 = BPF_JMP | BPF_X | BPF_JGE; -/// BPF opcode: `jlt dst, imm, +off` /// `PC += off if dst < imm`. -pub const JLT_IMM: u8 = BPF_JMP | BPF_K | BPF_JLT; -/// BPF opcode: `jlt dst, src, +off` /// `PC += off if dst < src`. -pub const JLT_REG: u8 = BPF_JMP | BPF_X | BPF_JLT; -/// BPF opcode: `jle dst, imm, +off` /// `PC += off if dst <= imm`. -pub const JLE_IMM: u8 = BPF_JMP | BPF_K | BPF_JLE; -/// BPF opcode: `jle dst, src, +off` /// `PC += off if dst <= src`. -pub const JLE_REG: u8 = BPF_JMP | BPF_X | BPF_JLE; -/// BPF opcode: `jset dst, imm, +off` /// `PC += off if dst & imm`. -pub const JSET_IMM: u8 = BPF_JMP | BPF_K | BPF_JSET; -/// BPF opcode: `jset dst, src, +off` /// `PC += off if dst & src`. -pub const JSET_REG: u8 = BPF_JMP | BPF_X | BPF_JSET; -/// BPF opcode: `jne dst, imm, +off` /// `PC += off if dst != imm`. -pub const JNE_IMM: u8 = BPF_JMP | BPF_K | BPF_JNE; -/// BPF opcode: `jne dst, src, +off` /// `PC += off if dst != src`. -pub const JNE_REG: u8 = BPF_JMP | BPF_X | BPF_JNE; -/// BPF opcode: `jsgt dst, imm, +off` /// `PC += off if dst > imm (signed)`. -pub const JSGT_IMM: u8 = BPF_JMP | BPF_K | BPF_JSGT; -/// BPF opcode: `jsgt dst, src, +off` /// `PC += off if dst > src (signed)`. -pub const JSGT_REG: u8 = BPF_JMP | BPF_X | BPF_JSGT; -/// BPF opcode: `jsge dst, imm, +off` /// `PC += off if dst >= imm (signed)`. -pub const JSGE_IMM: u8 = BPF_JMP | BPF_K | BPF_JSGE; -/// BPF opcode: `jsge dst, src, +off` /// `PC += off if dst >= src (signed)`. -pub const JSGE_REG: u8 = BPF_JMP | BPF_X | BPF_JSGE; -/// BPF opcode: `jslt dst, imm, +off` /// `PC += off if dst < imm (signed)`. -pub const JSLT_IMM: u8 = BPF_JMP | BPF_K | BPF_JSLT; -/// BPF opcode: `jslt dst, src, +off` /// `PC += off if dst < src (signed)`. -pub const JSLT_REG: u8 = BPF_JMP | BPF_X | BPF_JSLT; -/// BPF opcode: `jsle dst, imm, +off` /// `PC += off if dst <= imm (signed)`. -pub const JSLE_IMM: u8 = BPF_JMP | BPF_K | BPF_JSLE; -/// BPF opcode: `jsle dst, src, +off` /// `PC += off if dst <= src (signed)`. -pub const JSLE_REG: u8 = BPF_JMP | BPF_X | BPF_JSLE; - -/// BPF opcode: `jeq dst, imm, +off` /// `PC += off if (dst as u32) == imm`. -pub const JEQ_IMM32: u8 = BPF_JMP32 | BPF_K | BPF_JEQ; -/// BPF opcode: `jeq dst, src, +off` /// `PC += off if (dst as u32) == (src as u32)`. -pub const JEQ_REG32: u8 = BPF_JMP32 | BPF_X | BPF_JEQ; -/// BPF opcode: `jgt dst, imm, +off` /// `PC += off if (dst as u32) > imm`. -pub const JGT_IMM32: u8 = BPF_JMP32 | BPF_K | BPF_JGT; -/// BPF opcode: `jgt dst, src, +off` /// `PC += off if (dst as u32) > (src as u32)`. -pub const JGT_REG32: u8 = BPF_JMP32 | BPF_X | BPF_JGT; -/// BPF opcode: `jge dst, imm, +off` /// `PC += off if (dst as u32) >= imm`. -pub const JGE_IMM32: u8 = BPF_JMP32 | BPF_K | BPF_JGE; -/// BPF opcode: `jge dst, src, +off` /// `PC += off if (dst as u32) >= (src as u32)`. -pub const JGE_REG32: u8 = BPF_JMP32 | BPF_X | BPF_JGE; -/// BPF opcode: `jlt dst, imm, +off` /// `PC += off if (dst as u32) < imm`. -pub const JLT_IMM32: u8 = BPF_JMP32 | BPF_K | BPF_JLT; -/// BPF opcode: `jlt dst, src, +off` /// `PC += off if (dst as u32) < (src as u32)`. -pub const JLT_REG32: u8 = BPF_JMP32 | BPF_X | BPF_JLT; -/// BPF opcode: `jle dst, imm, +off` /// `PC += off if (dst as u32) <= imm`. -pub const JLE_IMM32: u8 = BPF_JMP32 | BPF_K | BPF_JLE; -/// BPF opcode: `jle dst, src, +off` /// `PC += off if (dst as u32) <= (src as u32)`. -pub const JLE_REG32: u8 = BPF_JMP32 | BPF_X | BPF_JLE; -/// BPF opcode: `jset dst, imm, +off` /// `PC += off if (dst as u32) & imm`. -pub const JSET_IMM32: u8 = BPF_JMP32 | BPF_K | BPF_JSET; -/// BPF opcode: `jset dst, src, +off` /// `PC += off if (dst as u32) & (src as u32)`. -pub const JSET_REG32: u8 = BPF_JMP32 | BPF_X | BPF_JSET; -/// BPF opcode: `jne dst, imm, +off` /// `PC += off if (dst as u32) != imm`. -pub const JNE_IMM32: u8 = BPF_JMP32 | BPF_K | BPF_JNE; -/// BPF opcode: `jne dst, src, +off` /// `PC += off if (dst as u32) != (src as u32)`. -pub const JNE_REG32: u8 = BPF_JMP32 | BPF_X | BPF_JNE; -/// BPF opcode: `jsgt dst, imm, +off` /// `PC += off if (dst as i32) > imm (signed)`. -pub const JSGT_IMM32: u8 = BPF_JMP32 | BPF_K | BPF_JSGT; -/// BPF opcode: `jsgt dst, src, +off` /// `PC += off if (dst as i32) > (src as i32) (signed)`. -pub const JSGT_REG32: u8 = BPF_JMP32 | BPF_X | BPF_JSGT; -/// BPF opcode: `jsge dst, imm, +off` /// `PC += off if (dst as i32) >= imm (signed)`. -pub const JSGE_IMM32: u8 = BPF_JMP32 | BPF_K | BPF_JSGE; -/// BPF opcode: `jsge dst, src, +off` /// `PC += off if (dst as i32) >= (src as i32) (signed)`. -pub const JSGE_REG32: u8 = BPF_JMP32 | BPF_X | BPF_JSGE; -/// BPF opcode: `jslt dst, imm, +off` /// `PC += off if (dst as i32) < imm (signed)`. -pub const JSLT_IMM32: u8 = BPF_JMP32 | BPF_K | BPF_JSLT; -/// BPF opcode: `jslt dst, src, +off` /// `PC += off if (dst as i32) < (src as i32) (signed)`. -pub const JSLT_REG32: u8 = BPF_JMP32 | BPF_X | BPF_JSLT; -/// BPF opcode: `jsle dst, imm, +off` /// `PC += off if (dst as i32) <= imm (signed)`. -pub const JSLE_IMM32: u8 = BPF_JMP32 | BPF_K | BPF_JSLE; -/// BPF opcode: `jsle dst, src, +off` /// `PC += off if (dst as i32) <= (src as i32) (signed)`. -pub const JSLE_REG32: u8 = BPF_JMP32 | BPF_X | BPF_JSLE; - -/// BPF opcode: `call imm` /// helper function call to helper with key `imm`. -pub const CALL: u8 = BPF_JMP | BPF_CALL; -/// BPF opcode: tail call. -pub const TAIL_CALL: u8 = BPF_JMP | BPF_X | BPF_CALL; -/// BPF opcode: `exit` /// `return r0`. -pub const EXIT: u8 = BPF_JMP | BPF_EXIT; - -// Used in JIT -/// Mask to extract the operation class from an operation code. -pub const BPF_CLS_MASK: u8 = 0x07; -/// Mask to extract the arithmetic operation code from an instruction operation code. -pub const BPF_ALU_OP_MASK: u8 = 0xf0; - -/// Prototype of an eBPF helper function. -pub type Helper = fn(u64, u64, u64, u64, u64) -> u64; - -/// An eBPF instruction. -/// -/// See for the Linux kernel -/// documentation about eBPF, or for a -/// more concise version. -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct Insn { - /// Operation code. - pub opc: u8, - /// Destination register operand. - pub dst: u8, - /// Source register operand. - pub src: u8, - /// Offset operand. - pub off: i16, - /// Immediate value operand. - pub imm: i32, -} - -impl Insn { - /// Turn an `Insn` back into an array of bytes. - /// - /// # Examples - /// - /// ``` - /// use rbpf::ebpf; - /// - /// let prog: &[u8] = &[ - /// 0xb7, 0x12, 0x56, 0x34, 0xde, 0xbc, 0x9a, 0x78, - /// ]; - /// let insn = ebpf::Insn { - /// opc: 0xb7, - /// dst: 2, - /// src: 1, - /// off: 0x3456, - /// imm: 0x789abcde - /// }; - /// assert_eq!(insn.to_array(), prog); - /// ``` - pub fn to_array(&self) -> [u8; INSN_SIZE] { - [ - self.opc, - self.src.wrapping_shl(4) | self.dst, - (self.off & 0xff) as u8, - self.off.wrapping_shr(8) as u8, - (self.imm & 0xff) as u8, - (self.imm & 0xff_00).wrapping_shr(8) as u8, - (self.imm as u32 & 0xff_00_00).wrapping_shr(16) as u8, - (self.imm as u32 & 0xff_00_00_00).wrapping_shr(24) as u8, - ] - } - - /// Turn an `Insn` into an vector of bytes. - /// - /// # Examples - /// - /// ``` - /// use rbpf::ebpf; - /// - /// let prog: Vec = vec![ - /// 0xb7, 0x12, 0x56, 0x34, 0xde, 0xbc, 0x9a, 0x78, - /// ]; - /// let insn = ebpf::Insn { - /// opc: 0xb7, - /// dst: 2, - /// src: 1, - /// off: 0x3456, - /// imm: 0x789abcde - /// }; - /// assert_eq!(insn.to_vec(), prog); - /// ``` - pub fn to_vec(&self) -> Vec { - vec![ - self.opc, - self.src.wrapping_shl(4) | self.dst, - (self.off & 0xff) as u8, - self.off.wrapping_shr(8) as u8, - (self.imm & 0xff) as u8, - (self.imm & 0xff_00).wrapping_shr(8) as u8, - (self.imm as u32 & 0xff_00_00).wrapping_shr(16) as u8, - (self.imm as u32 & 0xff_00_00_00).wrapping_shr(24) as u8, - ] - } -} - -/// Get the instruction at `idx` of an eBPF program. `idx` is the index (number) of the -/// instruction (not a byte offset). The first instruction has index 0. -/// -/// # Panics -/// -/// Panics if it is not possible to get the instruction (if idx is too high, or last instruction is -/// incomplete). -/// -/// # Examples -/// -/// ``` -/// use rbpf::ebpf; -/// -/// let prog = &[ -/// 0xb7, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -/// ]; -/// let insn = ebpf::get_insn(prog, 1); -/// assert_eq!(insn.opc, 0x95); -/// ``` -/// -/// The example below will panic, since the last instruction is not complete and cannot be loaded. -/// -/// ```rust,should_panic -/// use rbpf::ebpf; -/// -/// let prog = &[ -/// 0xb7, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -/// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00 // two bytes missing -/// ]; -/// let insn = ebpf::get_insn(prog, 1); -/// ``` -pub fn get_insn(prog: &[u8], idx: usize) -> Insn { - // This guard should not be needed in most cases, since the verifier already checks the program - // size, and indexes should be fine in the interpreter/JIT. But this function is publicly - // available and user can call it with any `idx`, so we have to check anyway. - if (idx + 1) * INSN_SIZE > prog.len() { - panic!( - "Error: cannot reach instruction at index {:?} in program containing {:?} bytes", - idx, - prog.len() - ); - } - Insn { - opc: prog[INSN_SIZE * idx], - dst: prog[INSN_SIZE * idx + 1] & 0x0f, - src: (prog[INSN_SIZE * idx + 1] & 0xf0) >> 4, - off: LittleEndian::read_i16(&prog[(INSN_SIZE * idx + 2)..]), - imm: LittleEndian::read_i32(&prog[(INSN_SIZE * idx + 4)..]), - } -} - -/// Return a vector of `struct Insn` built from a program. -/// -/// This is provided as a convenience for users wishing to manipulate a vector of instructions, for -/// example for dumping the program instruction after instruction with a custom format. -/// -/// Note that the two parts of `LD_DW_IMM` instructions (spanning on 64 bits) are considered as two -/// distinct instructions. -/// -/// # Examples -/// -/// ``` -/// use rbpf::ebpf; -/// -/// let prog = &[ -/// 0x18, 0x00, 0x00, 0x00, 0x88, 0x77, 0x66, 0x55, -/// 0x00, 0x00, 0x00, 0x00, 0x44, 0x33, 0x22, 0x11, -/// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -/// ]; -/// -/// let v = ebpf::to_insn_vec(prog); -/// assert_eq!(v, vec![ -/// ebpf::Insn { -/// opc: 0x18, -/// dst: 0, -/// src: 0, -/// off: 0, -/// imm: 0x55667788 -/// }, -/// ebpf::Insn { -/// opc: 0, -/// dst: 0, -/// src: 0, -/// off: 0, -/// imm: 0x11223344 -/// }, -/// ebpf::Insn { -/// opc: 0x95, -/// dst: 0, -/// src: 0, -/// off: 0, -/// imm: 0 -/// }, -/// ]); -/// ``` -pub fn to_insn_vec(prog: &[u8]) -> Vec { - if prog.len() % INSN_SIZE != 0 { - panic!( - "Error: eBPF program length must be a multiple of {:?} octets", - INSN_SIZE - ); - } - - let mut res = vec![]; - let mut insn_ptr: usize = 0; - - while insn_ptr * INSN_SIZE < prog.len() { - let insn = get_insn(prog, insn_ptr); - res.push(insn); - insn_ptr += 1; - } - res -} diff --git a/kernel/crates/rbpf/src/helpers.rs b/kernel/crates/rbpf/src/helpers.rs deleted file mode 100644 index 834bf8db..00000000 --- a/kernel/crates/rbpf/src/helpers.rs +++ /dev/null @@ -1,488 +0,0 @@ -// SPDX-License-Identifier: (Apache-2.0 OR MIT) -// Copyright 2015 Big Switch Networks, Inc -// (Algorithms for uBPF helpers, originally in C) -// Copyright 2016 6WIND S.A. -// (Translation to Rust, other helpers) - -//! This module implements some built-in helpers that can be called from within an eBPF program. -//! -//! These helpers may originate from several places: -//! -//! * Some of them mimic the helpers available in the Linux kernel. -//! * Some of them were proposed as example helpers in uBPF and they were adapted here. -//! * Other helpers may be specific to rbpf. -//! -//! The prototype for helpers is always the same: five `u64` as arguments, and a `u64` as a return -//! value. Hence some helpers have unused arguments, or return a 0 value in all cases, in order to -//! respect this convention. - -// Helpers associated to kernel helpers -// See also linux/include/uapi/linux/bpf.h in Linux kernel sources. - -// bpf_ktime_getns() - -/// Index of helper `bpf_ktime_getns()`, equivalent to `bpf_time_getns()`, in Linux kernel, see -/// . -pub const BPF_KTIME_GETNS_IDX: u32 = 5; - -/// Get monotonic time (since boot time) in nanoseconds. All arguments are unused. -/// -/// # Examples -/// -/// ``` -/// use rbpf::helpers; -/// -/// let t = helpers::bpf_time_getns(0, 0, 0, 0, 0); -/// let d = t / 10u64.pow(9) / 60 / 60 / 24; -/// let h = (t / 10u64.pow(9) / 60 / 60) % 24; -/// let m = (t / 10u64.pow(9) / 60 ) % 60; -/// let s = (t / 10u64.pow(9)) % 60; -/// let ns = t % 10u64.pow(9); -/// println!("Uptime: {:#x} == {} days {}:{}:{}, {} ns", t, d, h, m, s, ns); -/// ``` -#[allow(dead_code)] -#[allow(unused_variables)] -#[allow(deprecated)] -#[cfg(feature = "std")] -pub fn bpf_time_getns(unused1: u64, unused2: u64, unused3: u64, unused4: u64, unused5: u64) -> u64 { - time::precise_time_ns() -} - -// bpf_trace_printk() - -/// Index of helper `bpf_trace_printk()`, equivalent to `bpf_trace_printf()`, in Linux kernel, see -/// . -pub const BPF_TRACE_PRINTK_IDX: u32 = 6; - -/// Prints its **last three** arguments to standard output. The **first two** arguments are -/// **unused**. Returns the number of bytes written. -/// -/// By ignoring the first two arguments, it creates a helper that will have a behavior similar to -/// the one of the equivalent helper `bpf_trace_printk()` from Linux kernel. -/// -/// # Examples -/// -/// ``` -/// use rbpf::helpers; -/// -/// let res = helpers::bpf_trace_printf(0, 0, 1, 15, 32); -/// assert_eq!(res as usize, "bpf_trace_printf: 0x1, 0xf, 0x20\n".len()); -/// ``` -/// -/// This will print `bpf_trace_printf: 0x1, 0xf, 0x20`. -/// -/// The eBPF code needed to perform the call in this example would be nearly identical to the code -/// obtained by compiling the following code from C to eBPF with clang: -/// -/// ```c -/// #include -/// #include "path/to/linux/samples/bpf/bpf_helpers.h" -/// -/// int main(struct __sk_buff *skb) -/// { -/// // Only %d %u %x %ld %lu %lx %lld %llu %llx %p %s conversion specifiers allowed. -/// // See . -/// char *fmt = "bpf_trace_printk %llx, %llx, %llx\n"; -/// return bpf_trace_printk(fmt, sizeof(fmt), 1, 15, 32); -/// } -/// ``` -/// -/// This would equally print the three numbers in `/sys/kernel/debug/tracing` file each time the -/// program is run. -#[allow(dead_code)] -#[allow(unused_variables)] -#[cfg(feature = "std")] -pub fn bpf_trace_printf(unused1: u64, unused2: u64, arg3: u64, arg4: u64, arg5: u64) -> u64 { - println!("bpf_trace_printf: {arg3:#x}, {arg4:#x}, {arg5:#x}"); - let size_arg = |x| { - if x == 0 { - 1 - } else { - (x as f64).log(16.0).floor() as u64 + 1 - } - }; - "bpf_trace_printf: 0x, 0x, 0x\n".len() as u64 + size_arg(arg3) + size_arg(arg4) + size_arg(arg5) -} - -// Helpers coming from uBPF - -/// The idea is to assemble five bytes into a single `u64`. For compatibility with the helpers API, -/// each argument must be a `u64`. -/// -/// # Examples -/// -/// ``` -/// use rbpf::helpers; -/// -/// let gathered = helpers::gather_bytes(0x11, 0x22, 0x33, 0x44, 0x55); -/// assert_eq!(gathered, 0x1122334455); -/// ``` -pub fn gather_bytes(arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64) -> u64 { - arg1.wrapping_shl(32) - | arg2.wrapping_shl(24) - | arg3.wrapping_shl(16) - | arg4.wrapping_shl(8) - | arg5 -} - -/// Same as `void *memfrob(void *s, size_t n);` in `string.h` in C. See the GNU manual page (in -/// section 3) for `memfrob`. The memory is directly modified, and the helper returns 0 in all -/// cases. Arguments 3 to 5 are unused. -/// -/// # Examples -/// -/// ``` -/// use rbpf::helpers; -/// -/// let val: u64 = 0x112233; -/// let val_ptr = &val as *const u64; -/// -/// helpers::memfrob(val_ptr as u64, 8, 0, 0, 0); -/// assert_eq!(val, 0x2a2a2a2a2a3b0819); -/// helpers::memfrob(val_ptr as u64, 8, 0, 0, 0); -/// assert_eq!(val, 0x112233); -/// ``` -#[allow(unused_variables)] -pub fn memfrob(ptr: u64, len: u64, unused3: u64, unused4: u64, unused5: u64) -> u64 { - for i in 0..len { - unsafe { - let mut p = (ptr + i) as *mut u8; - *p ^= 0b101010; - } - } - 0 -} - -// TODO: Try again when asm!() is available in stable Rust. -// #![feature(asm)] -// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -// #[allow(unused_variables)] -// pub fn memfrob (ptr: u64, len: u64, arg3: u64, arg4: u64, arg5: u64) -> u64 { -// unsafe { -// asm!( -// "mov $0xf0, %rax" -// ::: "mov $0xf1, %rcx" -// ::: "mov $0xf2, %rdx" -// ::: "mov $0xf3, %rsi" -// ::: "mov $0xf4, %rdi" -// ::: "mov $0xf5, %r8" -// ::: "mov $0xf6, %r9" -// ::: "mov $0xf7, %r10" -// ::: "mov $0xf8, %r11" -// ); -// } -// 0 -// } - -/// Compute and return the square root of argument 1, cast as a float. Arguments 2 to 5 are -/// unused. -/// -/// # Examples -/// -/// ``` -/// use rbpf::helpers; -/// -/// let x = helpers::sqrti(9, 0, 0, 0, 0); -/// assert_eq!(x, 3); -/// ``` -#[allow(dead_code)] -#[allow(unused_variables)] -#[cfg(feature = "std")] // sqrt is only available when using `std` -pub fn sqrti(arg1: u64, unused2: u64, unused3: u64, unused4: u64, unused5: u64) -> u64 { - (arg1 as f64).sqrt() as u64 -} - -/// C-like `strcmp`, return 0 if the strings are equal, and a non-null value otherwise. -/// -/// # Examples -/// -/// ``` -/// use rbpf::helpers; -/// -/// let foo = "This is a string.\0".as_ptr() as u64; -/// let bar = "This is another sting.\0".as_ptr() as u64; -/// -/// assert!(helpers::strcmp(foo, foo, 0, 0, 0) == 0); -/// assert!(helpers::strcmp(foo, bar, 0, 0, 0) != 0); -/// ``` -#[allow(dead_code)] -#[allow(unused_variables)] -pub fn strcmp(arg1: u64, arg2: u64, arg3: u64, unused4: u64, unused5: u64) -> u64 { - // C-like strcmp, maybe shorter than converting the bytes to string and comparing? - if arg1 == 0 || arg2 == 0 { - return u64::MAX; - } - let mut a = arg1; - let mut b = arg2; - unsafe { - let mut a_val = *(a as *const u8); - let mut b_val = *(b as *const u8); - while a_val == b_val && a_val != 0 && b_val != 0 { - a += 1; - b += 1; - a_val = *(a as *const u8); - b_val = *(b as *const u8); - } - if a_val >= b_val { - (a_val - b_val) as u64 - } else { - (b_val - a_val) as u64 - } - } -} - -// Some additional helpers - -/// Returns a random u64 value comprised between `min` and `max` values (inclusive). Arguments 3 to -/// 5 are unused. -/// -/// Relies on `rand()` function from libc, so `libc::srand()` should be called once before this -/// helper is used. -/// -/// # Examples -/// -/// ``` -/// extern crate libc; -/// extern crate rbpf; -/// extern crate time; -/// -/// unsafe { -/// libc::srand(time::precise_time_ns() as u32) -/// } -/// -/// let n = rbpf::helpers::rand(3, 6, 0, 0, 0); -/// assert!(3 <= n && n <= 6); -/// ``` -#[allow(dead_code)] -#[allow(unused_variables)] -#[cfg(feature = "std")] -pub fn rand(min: u64, max: u64, unused3: u64, unused4: u64, unused5: u64) -> u64 { - let mut n = unsafe { (libc::rand() as u64).wrapping_shl(32) + libc::rand() as u64 }; - if min < max { - n = n % (max + 1 - min) + min; - }; - n -} -/// Prints the helper functions name and it's index. -#[cfg(feature = "std")] -pub fn show_helper() { - for (index, name) in BPF_FUNC_MAPPER.iter().enumerate() { - println!("{}:{}", index, name); - } -} - -/// See https://github.com/torvalds/linux/blob/master/include/uapi/linux/bpf.h -pub const BPF_FUNC_MAPPER: &[&str] = &[ - "unspec", - "map_lookup_elem", - "map_update_elem", - "map_delete_elem", - "probe_read", - "ktime_get_ns", - "trace_printk", - "get_prandom_u32", - "get_smp_processor_id", - "skb_store_bytes", - "l3_csum_replace", - "l4_csum_replace", - "tail_call", - "clone_redirect", - "get_current_pid_tgid", - "get_current_uid_gid", - "get_current_comm", - "get_cgroup_classid", - "skb_vlan_push", - "skb_vlan_pop", - "skb_get_tunnel_key", - "skb_set_tunnel_key", - "perf_event_read", - "redirect", - "get_route_realm", - "perf_event_output", - "skb_load_bytes", - "get_stackid", - "csum_diff", - "skb_get_tunnel_opt", - "skb_set_tunnel_opt", - "skb_change_proto", - "skb_change_type", - "skb_under_cgroup", - "get_hash_recalc", - "get_current_task", - "probe_write_user", - "current_task_under_cgroup", - "skb_change_tail", - "skb_pull_data", - "csum_update", - "set_hash_invalid", - "get_numa_node_id", - "skb_change_head", - "xdp_adjust_head", - "probe_read_str", - "get_socket_cookie", - "get_socket_uid", - "set_hash", - "setsockopt", - "skb_adjust_room", - "redirect_map", - "sk_redirect_map", - "sock_map_update", - "xdp_adjust_meta", - "perf_event_read_value", - "perf_prog_read_value", - "getsockopt", - "override_return", - "sock_ops_cb_flags_set", - "msg_redirect_map", - "msg_apply_bytes", - "msg_cork_bytes", - "msg_pull_data", - "bind", - "xdp_adjust_tail", - "skb_get_xfrm_state", - "get_stack", - "skb_load_bytes_relative", - "fib_lookup", - "sock_hash_update", - "msg_redirect_hash", - "sk_redirect_hash", - "lwt_push_encap", - "lwt_seg6_store_bytes", - "lwt_seg6_adjust_srh", - "lwt_seg6_action", - "rc_repeat", - "rc_keydown", - "skb_cgroup_id", - "get_current_cgroup_id", - "get_local_storage", - "sk_select_reuseport", - "skb_ancestor_cgroup_id", - "sk_lookup_tcp", - "sk_lookup_udp", - "sk_release", - "map_push_elem", - "map_pop_elem", - "map_peek_elem", - "msg_push_data", - "msg_pop_data", - "rc_pointer_rel", - "spin_lock", - "spin_unlock", - "sk_fullsock", - "tcp_sock", - "skb_ecn_set_ce", - "get_listener_sock", - "skc_lookup_tcp", - "tcp_check_syncookie", - "sysctl_get_name", - "sysctl_get_current_value", - "sysctl_get_new_value", - "sysctl_set_new_value", - "strtol", - "strtoul", - "sk_storage_get", - "sk_storage_delete", - "send_signal", - "tcp_gen_syncookie", - "skb_output", - "probe_read_user", - "probe_read_kernel", - "probe_read_user_str", - "probe_read_kernel_str", - "tcp_send_ack", - "send_signal_thread", - "jiffies64", - "read_branch_records", - "get_ns_current_pid_tgid", - "xdp_output", - "get_netns_cookie", - "get_current_ancestor_cgroup_id", - "sk_assign", - "ktime_get_boot_ns", - "seq_printf", - "seq_write", - "sk_cgroup_id", - "sk_ancestor_cgroup_id", - "ringbuf_output", - "ringbuf_reserve", - "ringbuf_submit", - "ringbuf_discard", - "ringbuf_query", - "csum_level", - "skc_to_tcp6_sock", - "skc_to_tcp_sock", - "skc_to_tcp_timewait_sock", - "skc_to_tcp_request_sock", - "skc_to_udp6_sock", - "get_task_stack", - "load_hdr_opt", - "store_hdr_opt", - "reserve_hdr_opt", - "inode_storage_get", - "inode_storage_delete", - "d_path", - "copy_from_user", - "snprintf_btf", - "seq_printf_btf", - "skb_cgroup_classid", - "redirect_neigh", - "per_cpu_ptr", - "this_cpu_ptr", - "redirect_peer", - "task_storage_get", - "task_storage_delete", - "get_current_task_btf", - "bprm_opts_set", - "ktime_get_coarse_ns", - "ima_inode_hash", - "sock_from_file", - "check_mtu", - "for_each_map_elem", - "snprintf", - "sys_bpf", - "btf_find_by_name_kind", - "sys_close", - "timer_init", - "timer_set_callback", - "timer_start", - "timer_cancel", - "get_func_ip", - "get_attach_cookie", - "task_pt_regs", - "get_branch_snapshot", - "trace_vprintk", - "skc_to_unix_sock", - "kallsyms_lookup_name", - "find_vma", - "loop", - "strncmp", - "get_func_arg", - "get_func_ret", - "get_func_arg_cnt", - "get_retval", - "set_retval", - "xdp_get_buff_len", - "xdp_load_bytes", - "xdp_store_bytes", - "copy_from_user_task", - "skb_set_tstamp", - "ima_file_hash", - "kptr_xchg", - "map_lookup_percpu_elem", - "skc_to_mptcp_sock", - "dynptr_from_mem", - "ringbuf_reserve_dynptr", - "ringbuf_submit_dynptr", - "ringbuf_discard_dynptr", - "dynptr_read", - "dynptr_write", - "dynptr_data", - "tcp_raw_gen_syncookie_ipv4", - "tcp_raw_gen_syncookie_ipv6", - "tcp_raw_check_syncookie_ipv4", - "tcp_raw_check_syncookie_ipv6", - "ktime_get_tai_ns", - "user_ringbuf_drain", - "cgrp_storage_get", - "cgrp_storage_delete", -]; diff --git a/kernel/crates/rbpf/src/insn_builder.rs b/kernel/crates/rbpf/src/insn_builder.rs deleted file mode 100644 index d67c431b..00000000 --- a/kernel/crates/rbpf/src/insn_builder.rs +++ /dev/null @@ -1,2199 +0,0 @@ -// SPDX-License-Identifier: (Apache-2.0 OR MIT) -// Copyright 2017 Alex Dukhno - -//! Module provides API to create eBPF programs by Rust programming language - -use alloc::{vec, vec::Vec}; - -use crate::ebpf::*; - -/// Represents single eBPF instruction -pub trait Instruction: Sized { - /// returns instruction opt code - fn opt_code_byte(&self) -> u8; - - /// returns destination register - fn get_dst(&self) -> u8 { - self.get_insn().dst - } - - /// returns source register - fn get_src(&self) -> u8 { - self.get_insn().src - } - - /// returns offset bytes - fn get_off(&self) -> i16 { - self.get_insn().off - } - - /// returns immediate value - fn get_imm(&self) -> i32 { - self.get_insn().imm - } - - /// sets destination register - fn set_dst(mut self, dst: u8) -> Self { - self.get_insn_mut().dst = dst; - self - } - - /// sets source register - fn set_src(mut self, src: u8) -> Self { - self.get_insn_mut().src = src; - self - } - - /// sets offset bytes - fn set_off(mut self, offset: i16) -> Self { - self.get_insn_mut().off = offset; - self - } - - /// sets immediate value - fn set_imm(mut self, imm: i32) -> Self { - self.get_insn_mut().imm = imm; - self - } - - /// get `ebpf::Insn` struct - fn get_insn(&self) -> &Insn; - - /// get mutable `ebpf::Insn` struct - fn get_insn_mut(&mut self) -> &mut Insn; -} - -/// General trait for `Instruction`s and `BpfCode`. -/// Provides functionality to transform `struct` into collection of bytes -pub trait IntoBytes { - /// type of targeted transformation - type Bytes; - - /// consume `Self` with transformation into `Self::Bytes` - fn into_bytes(self) -> Self::Bytes; -} - -/// General implementation of `IntoBytes` for `Instruction` -impl IntoBytes for &'_ I { - type Bytes = Vec; - - /// transform immutable reference of `Instruction` into `Vec` with size of 8 - /// [ 1 byte , 1 byte , 2 bytes, 4 bytes ] - /// [ OP_CODE, SRC_REG | DST_REG, OFFSET , IMMEDIATE ] - fn into_bytes(self) -> Self::Bytes { - let buffer = vec![ - self.opt_code_byte(), - self.get_src() << 4 | self.get_dst(), - self.get_off() as u8, - (self.get_off() >> 8) as u8, - self.get_imm() as u8, - (self.get_imm() >> 8) as u8, - (self.get_imm() >> 16) as u8, - (self.get_imm() >> 24) as u8, - ]; - buffer - } -} - -/// BPF instruction stack in byte representation -#[derive(Default)] -pub struct BpfCode { - instructions: Vec, -} - -impl BpfCode { - /// creates new empty BPF instruction stack - pub fn new() -> Self { - BpfCode { - instructions: vec![], - } - } - - /// create ADD instruction - pub fn add(&mut self, source: Source, arch: Arch) -> Move { - self.mov_internal(source, arch, OpBits::Add) - } - - /// create SUB instruction - pub fn sub(&mut self, source: Source, arch: Arch) -> Move { - self.mov_internal(source, arch, OpBits::Sub) - } - - /// create MUL instruction - pub fn mul(&mut self, source: Source, arch: Arch) -> Move { - self.mov_internal(source, arch, OpBits::Mul) - } - - /// create DIV instruction - pub fn div(&mut self, source: Source, arch: Arch) -> Move { - self.mov_internal(source, arch, OpBits::Div) - } - - /// create OR instruction - pub fn bit_or(&mut self, source: Source, arch: Arch) -> Move { - self.mov_internal(source, arch, OpBits::BitOr) - } - - /// create AND instruction - pub fn bit_and(&mut self, source: Source, arch: Arch) -> Move { - self.mov_internal(source, arch, OpBits::BitAnd) - } - - /// create LSHIFT instruction - pub fn left_shift(&mut self, source: Source, arch: Arch) -> Move { - self.mov_internal(source, arch, OpBits::LShift) - } - - /// create RSHIFT instruction - pub fn right_shift(&mut self, source: Source, arch: Arch) -> Move { - self.mov_internal(source, arch, OpBits::RShift) - } - - /// create NEGATE instruction - pub fn negate(&mut self, arch: Arch) -> Move { - self.mov_internal(Source::Imm, arch, OpBits::Negate) - } - - /// create MOD instruction - pub fn modulo(&mut self, source: Source, arch: Arch) -> Move { - self.mov_internal(source, arch, OpBits::Mod) - } - - /// create XOR instruction - pub fn bit_xor(&mut self, source: Source, arch: Arch) -> Move { - self.mov_internal(source, arch, OpBits::BitXor) - } - - /// create MOV instruction - pub fn mov(&mut self, source: Source, arch: Arch) -> Move { - self.mov_internal(source, arch, OpBits::Mov) - } - - /// create SIGNED RSHIFT instruction - pub fn signed_right_shift(&mut self, source: Source, arch: Arch) -> Move { - self.mov_internal(source, arch, OpBits::SignRShift) - } - - #[inline] - fn mov_internal(&mut self, source: Source, arch_bits: Arch, op_bits: OpBits) -> Move { - Move { - bpf_code: self, - src_bit: source, - op_bits, - arch_bits, - insn: Insn { - opc: 0x00, - dst: 0x00, - src: 0x00, - off: 0x00_00, - imm: 0x00_00_00_00, - }, - } - } - - /// create byte swap instruction - pub fn swap_bytes(&mut self, endian: Endian) -> SwapBytes { - SwapBytes { - bpf_code: self, - endian, - insn: Insn { - opc: 0x00, - dst: 0x00, - src: 0x00, - off: 0x00_00, - imm: 0x00_00_00_00, - }, - } - } - - /// create LOAD instruction, IMMEDIATE is the source - pub fn load(&mut self, mem_size: MemSize) -> Load { - self.load_internal(mem_size, Addressing::Imm, BPF_LD) - } - - /// create ABSOLUTE LOAD instruction - pub fn load_abs(&mut self, mem_size: MemSize) -> Load { - self.load_internal(mem_size, Addressing::Abs, BPF_LD) - } - - /// create INDIRECT LOAD instruction - pub fn load_ind(&mut self, mem_size: MemSize) -> Load { - self.load_internal(mem_size, Addressing::Ind, BPF_LD) - } - - /// create LOAD instruction, MEMORY is the source - pub fn load_x(&mut self, mem_size: MemSize) -> Load { - self.load_internal(mem_size, Addressing::Mem, BPF_LDX) - } - - #[inline] - fn load_internal(&mut self, mem_size: MemSize, addressing: Addressing, source: u8) -> Load { - Load { - bpf_code: self, - addressing, - mem_size, - source, - insn: Insn { - opc: 0x00, - dst: 0x00, - src: 0x00, - off: 0x00_00, - imm: 0x00_00_00_00, - }, - } - } - - /// creates STORE instruction, IMMEDIATE is the source - pub fn store(&mut self, mem_size: MemSize) -> Store { - self.store_internal(mem_size, BPF_IMM) - } - - /// creates STORE instruction, MEMORY is the source - pub fn store_x(&mut self, mem_size: MemSize) -> Store { - self.store_internal(mem_size, BPF_MEM | BPF_STX) - } - - #[inline] - fn store_internal(&mut self, mem_size: MemSize, source: u8) -> Store { - Store { - bpf_code: self, - mem_size, - source, - insn: Insn { - opc: 0x00, - dst: 0x00, - src: 0x00, - off: 0x00_00, - imm: 0x00_00_00_00, - }, - } - } - - /// create unconditional JMP instruction - pub fn jump_unconditional(&mut self) -> Jump { - self.jump_conditional(Cond::Abs, Source::Imm) - } - - /// create conditional JMP instruction - pub fn jump_conditional(&mut self, cond: Cond, src_bit: Source) -> Jump { - Jump { - bpf_code: self, - cond, - src_bit, - insn: Insn { - opc: 0x00, - dst: 0x00, - src: 0x00, - off: 0x00_00, - imm: 0x00_00_00_00, - }, - } - } - - /// create CALL instruction - pub fn call(&mut self) -> FunctionCall { - FunctionCall { - bpf_code: self, - insn: Insn { - opc: 0x00, - dst: 0x00, - src: 0x00, - off: 0x00_00, - imm: 0x00_00_00_00, - }, - } - } - - /// create EXIT instruction - pub fn exit(&mut self) -> Exit { - Exit { - bpf_code: self, - insn: Insn { - opc: 0x00, - dst: 0x00, - src: 0x00, - off: 0x00_00, - imm: 0x00_00_00_00, - }, - } - } -} - -/// Transform `BpfCode` into assemble representation -impl<'a> IntoBytes for &'a BpfCode { - type Bytes = &'a [u8]; - - /// returns `BpfCode` instruction stack as `&[u8]` - fn into_bytes(self) -> Self::Bytes { - self.instructions.as_slice() - } -} - -/// struct to represent `MOV ALU` instructions -pub struct Move<'i> { - bpf_code: &'i mut BpfCode, - src_bit: Source, - op_bits: OpBits, - arch_bits: Arch, - insn: Insn, -} - -impl<'i> Move<'i> { - /// push MOV instruction into BpfCode instruction stack - pub fn push(self) -> &'i mut BpfCode { - let mut asm = self.into_bytes(); - self.bpf_code.instructions.append(&mut asm); - self.bpf_code - } -} - -impl Instruction for Move<'_> { - fn opt_code_byte(&self) -> u8 { - let op_bits = self.op_bits as u8; - let src_bit = self.src_bit as u8; - let arch_bits = self.arch_bits as u8; - op_bits | src_bit | arch_bits - } - - fn get_insn_mut(&mut self) -> &mut Insn { - &mut self.insn - } - - fn get_insn(&self) -> &Insn { - &self.insn - } -} - -#[derive(Copy, Clone, PartialEq, Eq)] -/// The source of ALU and JMP instructions -pub enum Source { - /// immediate field will be used as a source - Imm = BPF_IMM as isize, - /// src register will be used as a source - Reg = BPF_X as isize, -} - -#[derive(Copy, Clone)] -enum OpBits { - Add = BPF_ADD as isize, - Sub = BPF_SUB as isize, - Mul = BPF_MUL as isize, - Div = BPF_DIV as isize, - BitOr = BPF_OR as isize, - BitAnd = BPF_AND as isize, - LShift = BPF_LSH as isize, - RShift = BPF_RSH as isize, - Negate = BPF_NEG as isize, - Mod = BPF_MOD as isize, - BitXor = BPF_XOR as isize, - Mov = BPF_MOV as isize, - SignRShift = BPF_ARSH as isize, -} - -#[derive(Copy, Clone)] -/// Architecture of instructions -pub enum Arch { - /// 64-bit instructions - X64 = BPF_ALU64 as isize, - /// 32-bit instructions - X32 = BPF_ALU as isize, -} - -/// struct representation of byte swap operation -pub struct SwapBytes<'i> { - bpf_code: &'i mut BpfCode, - endian: Endian, - insn: Insn, -} - -impl<'i> SwapBytes<'i> { - /// push bytes swap instruction into BpfCode instruction stack - pub fn push(self) -> &'i mut BpfCode { - let mut asm = self.into_bytes(); - self.bpf_code.instructions.append(&mut asm); - self.bpf_code - } -} - -impl Instruction for SwapBytes<'_> { - fn opt_code_byte(&self) -> u8 { - self.endian as u8 - } - - fn get_insn_mut(&mut self) -> &mut Insn { - &mut self.insn - } - - fn get_insn(&self) -> &Insn { - &self.insn - } -} - -#[derive(Copy, Clone)] -/// Bytes endian -pub enum Endian { - /// Little endian - Little = LE as isize, - /// Big endian - Big = BE as isize, -} - -/// struct representation of LOAD instructions -pub struct Load<'i> { - bpf_code: &'i mut BpfCode, - addressing: Addressing, - mem_size: MemSize, - source: u8, - insn: Insn, -} - -impl<'i> Load<'i> { - /// push LOAD instruction into BpfCode instruction stack - pub fn push(self) -> &'i mut BpfCode { - let mut asm = self.into_bytes(); - self.bpf_code.instructions.append(&mut asm); - self.bpf_code - } -} - -impl Instruction for Load<'_> { - fn opt_code_byte(&self) -> u8 { - let size = self.mem_size as u8; - let addressing = self.addressing as u8; - addressing | size | self.source - } - - fn get_insn(&self) -> &Insn { - &self.insn - } - - fn get_insn_mut(&mut self) -> &mut Insn { - &mut self.insn - } -} - -/// struct representation of STORE instructions -pub struct Store<'i> { - bpf_code: &'i mut BpfCode, - mem_size: MemSize, - source: u8, - insn: Insn, -} - -impl<'i> Store<'i> { - /// push STORE instruction into BpfCode instruction stack - pub fn push(self) -> &'i mut BpfCode { - let mut asm = self.into_bytes(); - self.bpf_code.instructions.append(&mut asm); - self.bpf_code - } -} - -impl Instruction for Store<'_> { - fn opt_code_byte(&self) -> u8 { - let size = self.mem_size as u8; - BPF_MEM | BPF_ST | size | self.source - } - - fn get_insn(&self) -> &Insn { - &self.insn - } - - fn get_insn_mut(&mut self) -> &mut Insn { - &mut self.insn - } -} - -#[derive(Copy, Clone)] -/// Memory size for LOAD and STORE instructions -pub enum MemSize { - /// 8-bit size - Byte = BPF_B as isize, - /// 16-bit size - HalfWord = BPF_H as isize, - /// 32-bit size - Word = BPF_W as isize, - /// 64-bit size - DoubleWord = BPF_DW as isize, -} - -#[derive(Copy, Clone)] -enum Addressing { - Imm = BPF_IMM as isize, - Abs = BPF_ABS as isize, - Ind = BPF_IND as isize, - Mem = BPF_MEM as isize, -} - -/// struct representation of JMP instructions -pub struct Jump<'i> { - bpf_code: &'i mut BpfCode, - cond: Cond, - src_bit: Source, - insn: Insn, -} - -impl<'i> Jump<'i> { - /// push JMP instruction into BpfCode instruction stack - pub fn push(self) -> &'i mut BpfCode { - let mut asm = self.into_bytes(); - self.bpf_code.instructions.append(&mut asm); - self.bpf_code - } -} - -impl Instruction for Jump<'_> { - fn opt_code_byte(&self) -> u8 { - let cmp: u8 = self.cond as u8; - let src_bit = self.src_bit as u8; - cmp | src_bit | BPF_JMP - } - - fn get_insn(&self) -> &Insn { - &self.insn - } - - fn get_insn_mut(&mut self) -> &mut Insn { - &mut self.insn - } -} - -#[derive(Copy, Clone, PartialEq, Eq)] -/// Conditions for JMP instructions -pub enum Cond { - /// Absolute or unconditional - Abs = BPF_JA as isize, - /// Jump if `==` - Equals = BPF_JEQ as isize, - /// Jump if `>` - Greater = BPF_JGT as isize, - /// Jump if `>=` - GreaterEquals = BPF_JGE as isize, - /// Jump if `<` - Lower = BPF_JLT as isize, - /// Jump if `<=` - LowerEquals = BPF_JLE as isize, - /// Jump if `src` & `dst` - BitAnd = BPF_JSET as isize, - /// Jump if `!=` - NotEquals = BPF_JNE as isize, - /// Jump if `>` (signed) - GreaterSigned = BPF_JSGT as isize, - /// Jump if `>=` (signed) - GreaterEqualsSigned = BPF_JSGE as isize, - /// Jump if `<` (signed) - LowerSigned = BPF_JSLT as isize, - /// Jump if `<=` (signed) - LowerEqualsSigned = BPF_JSLE as isize, -} - -/// struct representation of CALL instruction -pub struct FunctionCall<'i> { - bpf_code: &'i mut BpfCode, - insn: Insn, -} - -impl<'i> FunctionCall<'i> { - /// push CALL instruction into BpfCode instruction stack - pub fn push(self) -> &'i mut BpfCode { - let mut asm = self.into_bytes(); - self.bpf_code.instructions.append(&mut asm); - self.bpf_code - } -} - -impl Instruction for FunctionCall<'_> { - fn opt_code_byte(&self) -> u8 { - BPF_CALL | BPF_JMP - } - - fn get_insn(&self) -> &Insn { - &self.insn - } - - fn get_insn_mut(&mut self) -> &mut Insn { - &mut self.insn - } -} - -/// struct representation of EXIT instruction -pub struct Exit<'i> { - bpf_code: &'i mut BpfCode, - insn: Insn, -} - -impl<'i> Exit<'i> { - /// push EXIT instruction into BpfCode instruction stack - pub fn push(self) -> &'i mut BpfCode { - let mut asm = self.into_bytes(); - self.bpf_code.instructions.append(&mut asm); - self.bpf_code - } -} - -impl Instruction for Exit<'_> { - fn opt_code_byte(&self) -> u8 { - BPF_EXIT | BPF_JMP - } - - fn get_insn_mut(&mut self) -> &mut Insn { - &mut self.insn - } - - fn get_insn(&self) -> &Insn { - &self.insn - } -} - -#[cfg(test)] -mod tests { - #[cfg(test)] - mod special { - use super::super::*; - - #[test] - fn call_immediate() { - let mut program = BpfCode::new(); - program.call().set_imm(0x11_22_33_44).push(); - - assert_eq!( - program.into_bytes(), - &[0x85, 0x00, 0x00, 0x00, 0x44, 0x33, 0x22, 0x11] - ); - } - - #[test] - fn exit_operation() { - let mut program = BpfCode::new(); - program.exit().push(); - - assert_eq!( - program.into_bytes(), - &[0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - } - - #[cfg(test)] - mod jump_instructions { - #[cfg(test)] - mod register { - use super::super::super::*; - - #[test] - fn jump_on_dst_equals_src() { - let mut program = BpfCode::new(); - program - .jump_conditional(Cond::Equals, Source::Reg) - .set_dst(0x01) - .set_src(0x02) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x1d, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn jump_on_dst_greater_than_src() { - let mut program = BpfCode::new(); - program - .jump_conditional(Cond::Greater, Source::Reg) - .set_dst(0x03) - .set_src(0x02) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x2d, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn jump_on_dst_greater_or_equals_to_src() { - let mut program = BpfCode::new(); - program - .jump_conditional(Cond::GreaterEquals, Source::Reg) - .set_dst(0x04) - .set_src(0x01) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x3d, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn jump_on_dst_lower_than_src() { - let mut program = BpfCode::new(); - program - .jump_conditional(Cond::Lower, Source::Reg) - .set_dst(0x03) - .set_src(0x02) - .push(); - - assert_eq!( - program.into_bytes(), - &[0xad, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn jump_on_dst_lower_or_equals_to_src() { - let mut program = BpfCode::new(); - program - .jump_conditional(Cond::LowerEquals, Source::Reg) - .set_dst(0x04) - .set_src(0x01) - .push(); - - assert_eq!( - program.into_bytes(), - &[0xbd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn jump_on_dst_bit_and_with_src_not_equal_zero() { - let mut program = BpfCode::new(); - program - .jump_conditional(Cond::BitAnd, Source::Reg) - .set_dst(0x05) - .set_src(0x02) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x4d, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn jump_on_dst_not_equals_src() { - let mut program = BpfCode::new(); - program - .jump_conditional(Cond::NotEquals, Source::Reg) - .set_dst(0x03) - .set_src(0x05) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x5d, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn jump_on_dst_greater_than_src_signed() { - let mut program = BpfCode::new(); - program - .jump_conditional(Cond::GreaterSigned, Source::Reg) - .set_dst(0x04) - .set_src(0x01) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x6d, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn jump_on_dst_greater_or_equals_src_signed() { - let mut program = BpfCode::new(); - program - .jump_conditional(Cond::GreaterEqualsSigned, Source::Reg) - .set_dst(0x01) - .set_src(0x03) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x7d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn jump_on_dst_lower_than_src_signed() { - let mut program = BpfCode::new(); - program - .jump_conditional(Cond::LowerSigned, Source::Reg) - .set_dst(0x04) - .set_src(0x01) - .push(); - - assert_eq!( - program.into_bytes(), - &[0xcd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn jump_on_dst_lower_or_equals_src_signed() { - let mut program = BpfCode::new(); - program - .jump_conditional(Cond::LowerEqualsSigned, Source::Reg) - .set_dst(0x01) - .set_src(0x03) - .push(); - - assert_eq!( - program.into_bytes(), - &[0xdd, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - } - - #[cfg(test)] - mod immediate { - use super::super::super::*; - - #[test] - fn jump_to_label() { - let mut program = BpfCode::new(); - program.jump_unconditional().set_off(0x00_11).push(); - - assert_eq!( - program.into_bytes(), - &[0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn jump_on_dst_equals_const() { - let mut program = BpfCode::new(); - program - .jump_conditional(Cond::Equals, Source::Imm) - .set_dst(0x01) - .set_imm(0x00_11_22_33) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x15, 0x01, 0x00, 0x00, 0x33, 0x22, 0x11, 0x00] - ); - } - - #[test] - fn jump_on_dst_greater_than_const() { - let mut program = BpfCode::new(); - program - .jump_conditional(Cond::Greater, Source::Imm) - .set_dst(0x02) - .set_imm(0x00_11_00_11) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x25, 0x02, 0x00, 0x00, 0x11, 0x00, 0x11, 0x00] - ); - } - - #[test] - fn jump_on_dst_greater_or_equals_to_const() { - let mut program = BpfCode::new(); - program - .jump_conditional(Cond::GreaterEquals, Source::Imm) - .set_dst(0x04) - .set_imm(0x00_22_11_00) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x35, 0x04, 0x00, 0x00, 0x00, 0x11, 0x22, 0x00] - ); - } - - #[test] - fn jump_on_dst_lower_than_const() { - let mut program = BpfCode::new(); - program - .jump_conditional(Cond::Lower, Source::Imm) - .set_dst(0x02) - .set_imm(0x00_11_00_11) - .push(); - - assert_eq!( - program.into_bytes(), - &[0xa5, 0x02, 0x00, 0x00, 0x11, 0x00, 0x11, 0x00] - ); - } - - #[test] - fn jump_on_dst_lower_or_equals_to_const() { - let mut program = BpfCode::new(); - program - .jump_conditional(Cond::LowerEquals, Source::Imm) - .set_dst(0x04) - .set_imm(0x00_22_11_00) - .push(); - - assert_eq!( - program.into_bytes(), - &[0xb5, 0x04, 0x00, 0x00, 0x00, 0x11, 0x22, 0x00] - ); - } - - #[test] - fn jump_on_dst_bit_and_with_const_not_equal_zero() { - let mut program = BpfCode::new(); - program - .jump_conditional(Cond::BitAnd, Source::Imm) - .set_dst(0x05) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x45, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn jump_on_dst_not_equals_const() { - let mut program = BpfCode::new(); - program - .jump_conditional(Cond::NotEquals, Source::Imm) - .set_dst(0x03) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x55, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn jump_on_dst_greater_than_const_signed() { - let mut program = BpfCode::new(); - program - .jump_conditional(Cond::GreaterSigned, Source::Imm) - .set_dst(0x04) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x65, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn jump_on_dst_greater_or_equals_src_signed() { - let mut program = BpfCode::new(); - program - .jump_conditional(Cond::GreaterEqualsSigned, Source::Imm) - .set_dst(0x01) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x75, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn jump_on_dst_lower_than_const_signed() { - let mut program = BpfCode::new(); - program - .jump_conditional(Cond::LowerSigned, Source::Imm) - .set_dst(0x04) - .push(); - - assert_eq!( - program.into_bytes(), - &[0xc5, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn jump_on_dst_lower_or_equals_src_signed() { - let mut program = BpfCode::new(); - program - .jump_conditional(Cond::LowerEqualsSigned, Source::Imm) - .set_dst(0x01) - .push(); - - assert_eq!( - program.into_bytes(), - &[0xd5, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - } - } - - #[cfg(test)] - mod store_instructions { - use super::super::*; - - #[test] - fn store_word_from_dst_into_immediate_address() { - let mut program = BpfCode::new(); - program - .store(MemSize::Word) - .set_dst(0x01) - .set_off(0x00_11) - .set_imm(0x11_22_33_44) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x62, 0x01, 0x11, 0x00, 0x44, 0x33, 0x22, 0x11] - ); - } - - #[test] - fn store_half_word_from_dst_into_immediate_address() { - let mut program = BpfCode::new(); - program - .store(MemSize::HalfWord) - .set_dst(0x02) - .set_off(0x11_22) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x6a, 0x02, 0x22, 0x11, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn store_byte_from_dst_into_immediate_address() { - let mut program = BpfCode::new(); - program.store(MemSize::Byte).push(); - - assert_eq!( - program.into_bytes(), - &[0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn store_double_word_from_dst_into_immediate_address() { - let mut program = BpfCode::new(); - program.store(MemSize::DoubleWord).push(); - - assert_eq!( - program.into_bytes(), - &[0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn store_word_from_dst_into_src_address() { - let mut program = BpfCode::new(); - program - .store_x(MemSize::Word) - .set_dst(0x01) - .set_src(0x02) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x63, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn store_half_word_from_dst_into_src_address() { - let mut program = BpfCode::new(); - program.store_x(MemSize::HalfWord).push(); - - assert_eq!( - program.into_bytes(), - &[0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn store_byte_from_dst_into_src_address() { - let mut program = BpfCode::new(); - program.store_x(MemSize::Byte).push(); - - assert_eq!( - program.into_bytes(), - &[0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn store_double_word_from_dst_into_src_address() { - let mut program = BpfCode::new(); - program.store_x(MemSize::DoubleWord).push(); - - assert_eq!( - program.into_bytes(), - &[0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - } - - #[cfg(test)] - mod load_instructions { - #[cfg(test)] - mod register { - use super::super::super::*; - - #[test] - fn load_word_from_set_src_with_offset() { - let mut program = BpfCode::new(); - program - .load_x(MemSize::Word) - .set_dst(0x01) - .set_src(0x02) - .set_off(0x00_02) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x61, 0x21, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn load_half_word_from_set_src_with_offset() { - let mut program = BpfCode::new(); - program - .load_x(MemSize::HalfWord) - .set_dst(0x02) - .set_src(0x01) - .set_off(0x11_22) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x69, 0x12, 0x22, 0x11, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn load_byte_from_set_src_with_offset() { - let mut program = BpfCode::new(); - program - .load_x(MemSize::Byte) - .set_dst(0x01) - .set_src(0x04) - .set_off(0x00_11) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x71, 0x41, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn load_double_word_from_set_src_with_offset() { - let mut program = BpfCode::new(); - program - .load_x(MemSize::DoubleWord) - .set_dst(0x04) - .set_src(0x05) - .set_off(0x44_55) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x79, 0x54, 0x55, 0x44, 0x00, 0x00, 0x00, 0x00] - ); - } - } - - #[cfg(test)] - mod immediate { - use super::super::super::*; - - #[test] - fn load_double_word() { - let mut program = BpfCode::new(); - program - .load(MemSize::DoubleWord) - .set_dst(0x01) - .set_imm(0x00_01_02_03) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x18, 0x01, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00] - ); - } - - #[test] - fn load_abs_word() { - let mut program = BpfCode::new(); - program.load_abs(MemSize::Word).push(); - - assert_eq!( - program.into_bytes(), - &[0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn load_abs_half_word() { - let mut program = BpfCode::new(); - program.load_abs(MemSize::HalfWord).set_dst(0x05).push(); - - assert_eq!( - program.into_bytes(), - &[0x28, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn load_abs_byte() { - let mut program = BpfCode::new(); - program.load_abs(MemSize::Byte).set_dst(0x01).push(); - - assert_eq!( - program.into_bytes(), - &[0x30, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn load_abs_double_word() { - let mut program = BpfCode::new(); - program - .load_abs(MemSize::DoubleWord) - .set_dst(0x01) - .set_imm(0x01_02_03_04) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x38, 0x01, 0x00, 0x00, 0x04, 0x03, 0x02, 0x01] - ); - } - - #[test] - fn load_indirect_word() { - let mut program = BpfCode::new(); - program.load_ind(MemSize::Word).push(); - - assert_eq!( - program.into_bytes(), - &[0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn load_indirect_half_word() { - let mut program = BpfCode::new(); - program.load_ind(MemSize::HalfWord).push(); - - assert_eq!( - program.into_bytes(), - &[0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn load_indirect_byte() { - let mut program = BpfCode::new(); - program.load_ind(MemSize::Byte).push(); - - assert_eq!( - program.into_bytes(), - &[0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn load_indirect_double_word() { - let mut program = BpfCode::new(); - program.load_ind(MemSize::DoubleWord).push(); - - assert_eq!( - program.into_bytes(), - &[0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - } - } - - #[cfg(test)] - mod byte_swap_instructions { - use super::super::*; - - #[test] - fn convert_host_to_little_endian_16bits() { - let mut program = BpfCode::new(); - program - .swap_bytes(Endian::Little) - .set_dst(0x01) - .set_imm(0x00_00_00_10) - .push(); - - assert_eq!( - program.into_bytes(), - &[0xd4, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn convert_host_to_little_endian_32bits() { - let mut program = BpfCode::new(); - program - .swap_bytes(Endian::Little) - .set_dst(0x02) - .set_imm(0x00_00_00_20) - .push(); - - assert_eq!( - program.into_bytes(), - &[0xd4, 0x02, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn convert_host_to_little_endian_64bit() { - let mut program = BpfCode::new(); - program - .swap_bytes(Endian::Little) - .set_dst(0x03) - .set_imm(0x00_00_00_40) - .push(); - - assert_eq!( - program.into_bytes(), - &[0xd4, 0x03, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn convert_host_to_big_endian_16bits() { - let mut program = BpfCode::new(); - program - .swap_bytes(Endian::Big) - .set_dst(0x01) - .set_imm(0x00_00_00_10) - .push(); - - assert_eq!( - program.into_bytes(), - &[0xdc, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn convert_host_to_big_endian_32bits() { - let mut program = BpfCode::new(); - program - .swap_bytes(Endian::Big) - .set_dst(0x02) - .set_imm(0x00_00_00_20) - .push(); - - assert_eq!( - program.into_bytes(), - &[0xdc, 0x02, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn convert_host_to_big_endian_64bit() { - let mut program = BpfCode::new(); - program - .swap_bytes(Endian::Big) - .set_dst(0x03) - .set_imm(0x00_00_00_40) - .push(); - - assert_eq!( - program.into_bytes(), - &[0xdc, 0x03, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00] - ); - } - } - - #[cfg(test)] - mod moves_instructions { - #[cfg(test)] - mod arch_x64 { - #[cfg(test)] - mod immediate { - use super::super::super::super::*; - - #[test] - fn move_and_add_const_to_register() { - let mut program = BpfCode::new(); - program - .add(Source::Imm, Arch::X64) - .set_dst(0x02) - .set_imm(0x01_02_03_04) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x07, 0x02, 0x00, 0x00, 0x04, 0x03, 0x02, 0x01] - ); - } - - #[test] - fn move_sub_const_to_register() { - let mut program = BpfCode::new(); - program - .sub(Source::Imm, Arch::X64) - .set_dst(0x04) - .set_imm(0x00_01_02_03) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x17, 0x04, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00] - ); - } - - #[test] - fn move_mul_const_to_register() { - let mut program = BpfCode::new(); - program - .mul(Source::Imm, Arch::X64) - .set_dst(0x05) - .set_imm(0x04_03_02_01) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x27, 0x05, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04] - ); - } - - #[test] - fn move_div_constant_to_register() { - let mut program = BpfCode::new(); - program - .div(Source::Imm, Arch::X64) - .set_dst(0x02) - .set_imm(0x00_ff_00_ff) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x37, 0x02, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00] - ); - } - - #[test] - fn move_bit_or_const_to_register() { - let mut program = BpfCode::new(); - program - .bit_or(Source::Imm, Arch::X64) - .set_dst(0x02) - .set_imm(0x00_11_00_22) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x47, 0x02, 0x00, 0x00, 0x22, 0x00, 0x11, 0x00] - ); - } - - #[test] - fn move_bit_and_const_to_register() { - let mut program = BpfCode::new(); - program - .bit_and(Source::Imm, Arch::X64) - .set_dst(0x02) - .set_imm(0x11_22_33_44) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x57, 0x02, 0x00, 0x00, 0x44, 0x33, 0x22, 0x11] - ); - } - - #[test] - fn move_left_shift_const_to_register() { - let mut program = BpfCode::new(); - program - .left_shift(Source::Imm, Arch::X64) - .set_dst(0x01) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x67, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn move_logical_right_shift_const_to_register() { - let mut program = BpfCode::new(); - program - .right_shift(Source::Imm, Arch::X64) - .set_dst(0x01) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x77, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn move_negate_register() { - let mut program = BpfCode::new(); - program.negate(Arch::X64).set_dst(0x02).push(); - - assert_eq!( - program.into_bytes(), - &[0x87, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn move_mod_const_to_register() { - let mut program = BpfCode::new(); - program.modulo(Source::Imm, Arch::X64).set_dst(0x02).push(); - - assert_eq!( - program.into_bytes(), - &[0x97, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn move_bit_xor_const_to_register() { - let mut program = BpfCode::new(); - program.bit_xor(Source::Imm, Arch::X64).set_dst(0x03).push(); - - assert_eq!( - program.into_bytes(), - &[0xa7, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn move_const_to_register() { - let mut program = BpfCode::new(); - program - .mov(Source::Imm, Arch::X64) - .set_dst(0x01) - .set_imm(0x00_00_00_FF) - .push(); - - assert_eq!( - program.into_bytes(), - &[0xb7, 0x01, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn move_signed_right_shift_const_to_register() { - let mut program = BpfCode::new(); - program - .signed_right_shift(Source::Imm, Arch::X64) - .set_dst(0x05) - .push(); - - assert_eq!( - program.into_bytes(), - &[0xc7, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - } - - #[cfg(test)] - mod register { - use super::super::super::super::*; - - #[test] - fn move_and_add_from_register() { - let mut program = BpfCode::new(); - program - .add(Source::Reg, Arch::X64) - .set_dst(0x03) - .set_src(0x02) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x0f, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn move_sub_from_register_to_register() { - let mut program = BpfCode::new(); - program - .sub(Source::Reg, Arch::X64) - .set_dst(0x03) - .set_src(0x04) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x1f, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn move_mul_from_register_to_register() { - let mut program = BpfCode::new(); - program - .mul(Source::Reg, Arch::X64) - .set_dst(0x04) - .set_src(0x03) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x2f, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn move_div_from_register_to_register() { - let mut program = BpfCode::new(); - program - .div(Source::Reg, Arch::X64) - .set_dst(0x01) - .set_src(0x00) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x3f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn move_bit_or_from_register_to_register() { - let mut program = BpfCode::new(); - program - .bit_or(Source::Reg, Arch::X64) - .set_dst(0x03) - .set_src(0x01) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x4f, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn move_bit_and_from_register_to_register() { - let mut program = BpfCode::new(); - program - .bit_and(Source::Reg, Arch::X64) - .set_dst(0x03) - .set_src(0x02) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x5f, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn move_left_shift_from_register_to_register() { - let mut program = BpfCode::new(); - program - .left_shift(Source::Reg, Arch::X64) - .set_dst(0x02) - .set_src(0x03) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x6f, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn move_logical_right_shift_from_register_to_register() { - let mut program = BpfCode::new(); - program - .right_shift(Source::Reg, Arch::X64) - .set_dst(0x02) - .set_src(0x04) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x7f, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn move_mod_from_register_to_register() { - let mut program = BpfCode::new(); - program - .modulo(Source::Reg, Arch::X64) - .set_dst(0x01) - .set_src(0x02) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x9f, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn move_bit_xor_from_register_to_register() { - let mut program = BpfCode::new(); - program - .bit_xor(Source::Reg, Arch::X64) - .set_dst(0x02) - .set_src(0x04) - .push(); - - assert_eq!( - program.into_bytes(), - &[0xaf, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn move_from_register_to_another_register() { - let mut program = BpfCode::new(); - program.mov(Source::Reg, Arch::X64).set_src(0x01).push(); - - assert_eq!( - program.into_bytes(), - &[0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn move_signed_right_shift_from_register_to_register() { - let mut program = BpfCode::new(); - program - .signed_right_shift(Source::Reg, Arch::X64) - .set_dst(0x02) - .set_src(0x03) - .push(); - - assert_eq!( - program.into_bytes(), - &[0xcf, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - } - } - - #[cfg(test)] - mod arch_x32 { - #[cfg(test)] - mod immediate { - use super::super::super::super::*; - - #[test] - fn move_and_add_const_to_register() { - let mut program = BpfCode::new(); - program - .add(Source::Imm, Arch::X32) - .set_dst(0x02) - .set_imm(0x01_02_03_04) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x04, 0x02, 0x00, 0x00, 0x04, 0x03, 0x02, 0x01] - ); - } - - #[test] - fn move_sub_const_to_register() { - let mut program = BpfCode::new(); - program - .sub(Source::Imm, Arch::X32) - .set_dst(0x04) - .set_imm(0x00_01_02_03) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x14, 0x04, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00] - ); - } - - #[test] - fn move_mul_const_to_register() { - let mut program = BpfCode::new(); - program - .mul(Source::Imm, Arch::X32) - .set_dst(0x05) - .set_imm(0x04_03_02_01) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x24, 0x05, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04] - ); - } - - #[test] - fn move_div_constant_to_register() { - let mut program = BpfCode::new(); - program - .div(Source::Imm, Arch::X32) - .set_dst(0x02) - .set_imm(0x00_ff_00_ff) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x34, 0x02, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00] - ); - } - - #[test] - fn move_bit_or_const_to_register() { - let mut program = BpfCode::new(); - program - .bit_or(Source::Imm, Arch::X32) - .set_dst(0x02) - .set_imm(0x00_11_00_22) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x44, 0x02, 0x00, 0x00, 0x22, 0x00, 0x11, 0x00] - ); - } - - #[test] - fn move_bit_and_const_to_register() { - let mut program = BpfCode::new(); - program - .bit_and(Source::Imm, Arch::X32) - .set_dst(0x02) - .set_imm(0x11_22_33_44) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x54, 0x02, 0x00, 0x00, 0x44, 0x33, 0x22, 0x11] - ); - } - - #[test] - fn move_left_shift_const_to_register() { - let mut program = BpfCode::new(); - program - .left_shift(Source::Imm, Arch::X32) - .set_dst(0x01) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x64, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn move_logical_right_shift_const_to_register() { - let mut program = BpfCode::new(); - program - .right_shift(Source::Imm, Arch::X32) - .set_dst(0x01) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x74, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn move_negate_register() { - let mut program = BpfCode::new(); - program.negate(Arch::X32).set_dst(0x02).push(); - - assert_eq!( - program.into_bytes(), - &[0x84, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn move_mod_const_to_register() { - let mut program = BpfCode::new(); - program.modulo(Source::Imm, Arch::X32).set_dst(0x02).push(); - - assert_eq!( - program.into_bytes(), - &[0x94, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn move_bit_xor_const_to_register() { - let mut program = BpfCode::new(); - program.bit_xor(Source::Imm, Arch::X32).set_dst(0x03).push(); - - assert_eq!( - program.into_bytes(), - &[0xa4, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn move_const_to_register() { - let mut program = BpfCode::new(); - program - .mov(Source::Imm, Arch::X32) - .set_dst(0x01) - .set_imm(0x00_00_00_FF) - .push(); - - assert_eq!( - program.into_bytes(), - &[0xb4, 0x01, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn move_signed_right_shift_const_to_register() { - let mut program = BpfCode::new(); - program - .signed_right_shift(Source::Imm, Arch::X32) - .set_dst(0x05) - .push(); - - assert_eq!( - program.into_bytes(), - &[0xc4, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - } - - #[cfg(test)] - mod register { - use super::super::super::super::*; - - #[test] - fn move_and_add_from_register() { - let mut program = BpfCode::new(); - program - .add(Source::Reg, Arch::X32) - .set_dst(0x03) - .set_src(0x02) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x0c, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn move_sub_from_register_to_register() { - let mut program = BpfCode::new(); - program - .sub(Source::Reg, Arch::X32) - .set_dst(0x03) - .set_src(0x04) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x1c, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn move_mul_from_register_to_register() { - let mut program = BpfCode::new(); - program - .mul(Source::Reg, Arch::X32) - .set_dst(0x04) - .set_src(0x03) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x2c, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn move_div_from_register_to_register() { - let mut program = BpfCode::new(); - program - .div(Source::Reg, Arch::X32) - .set_dst(0x01) - .set_src(0x00) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x3c, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn move_bit_or_from_register_to_register() { - let mut program = BpfCode::new(); - program - .bit_or(Source::Reg, Arch::X32) - .set_dst(0x03) - .set_src(0x01) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x4c, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn move_bit_and_from_register_to_register() { - let mut program = BpfCode::new(); - program - .bit_and(Source::Reg, Arch::X32) - .set_dst(0x03) - .set_src(0x02) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x5c, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn move_left_shift_from_register_to_register() { - let mut program = BpfCode::new(); - program - .left_shift(Source::Reg, Arch::X32) - .set_dst(0x02) - .set_src(0x03) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x6c, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn move_logical_right_shift_from_register_to_register() { - let mut program = BpfCode::new(); - program - .right_shift(Source::Reg, Arch::X32) - .set_dst(0x02) - .set_src(0x04) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x7c, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn move_mod_from_register_to_register() { - let mut program = BpfCode::new(); - program - .modulo(Source::Reg, Arch::X32) - .set_dst(0x01) - .set_src(0x02) - .push(); - - assert_eq!( - program.into_bytes(), - &[0x9c, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn move_bit_xor_from_register_to_register() { - let mut program = BpfCode::new(); - program - .bit_xor(Source::Reg, Arch::X32) - .set_dst(0x02) - .set_src(0x04) - .push(); - - assert_eq!( - program.into_bytes(), - &[0xac, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn move_from_register_to_another_register() { - let mut program = BpfCode::new(); - program - .mov(Source::Reg, Arch::X32) - .set_dst(0x00) - .set_src(0x01) - .push(); - - assert_eq!( - program.into_bytes(), - &[0xbc, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - - #[test] - fn move_signed_right_shift_from_register_to_register() { - let mut program = BpfCode::new(); - program - .signed_right_shift(Source::Reg, Arch::X32) - .set_dst(0x02) - .set_src(0x03) - .push(); - - assert_eq!( - program.into_bytes(), - &[0xcc, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] - ); - } - } - } - } - - #[cfg(test)] - mod programs { - use super::super::*; - - #[test] - fn example_from_assembler() { - let mut program = BpfCode::new(); - program - .add(Source::Imm, Arch::X64) - .set_dst(1) - .set_imm(0x605) - .push() - .mov(Source::Imm, Arch::X64) - .set_dst(2) - .set_imm(0x32) - .push() - .mov(Source::Reg, Arch::X64) - .set_src(0) - .set_dst(1) - .push() - .swap_bytes(Endian::Big) - .set_dst(0) - .set_imm(0x10) - .push() - .negate(Arch::X64) - .set_dst(2) - .push() - .exit() - .push(); - - let bytecode = program.into_bytes(); - let ref_prog = &[ - 0x07, 0x01, 0x00, 0x00, 0x05, 0x06, 0x00, 0x00, 0xb7, 0x02, 0x00, 0x00, 0x32, 0x00, - 0x00, 0x00, 0xbf, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x87, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ]; - // cargo says: "`[{integer}; 48]` cannot be formatted using `{:?}` - // because it doesn't implement `std::fmt::Debug`" - // So let's check in two steps. - assert_eq!(bytecode[..32], ref_prog[..32]); - assert_eq!(bytecode[33..], ref_prog[33..]); - } - } -} diff --git a/kernel/crates/rbpf/src/interpreter.rs b/kernel/crates/rbpf/src/interpreter.rs deleted file mode 100644 index 68b9878e..00000000 --- a/kernel/crates/rbpf/src/interpreter.rs +++ /dev/null @@ -1,708 +0,0 @@ -// SPDX-License-Identifier: (Apache-2.0 OR MIT) -// Derived from uBPF -// Copyright 2015 Big Switch Networks, Inc -// (uBPF: VM architecture, parts of the interpreter, originally in C) -// Copyright 2016 6WIND S.A. -// (Translation to Rust, MetaBuff/multiple classes addition, hashmaps for helpers) - -use crate::{ - ebpf::{self, Insn}, - helpers::BPF_FUNC_MAPPER, - stack::StackFrame, - *, -}; - -#[cfg(not(feature = "user"))] -#[allow(unused)] -fn check_mem( - addr: u64, - len: usize, - access_type: &str, - insn_ptr: usize, - mbuff: &[u8], - mem: &[u8], - stack: &[u8], -) -> Result<(), Error> { - log::trace!( - "check_mem: addr {:#x}, len {}, access_type {}, insn_ptr {}", - addr, - len, - access_type, - insn_ptr - ); - log::trace!( - "check_mem: mbuff: {:#x}/{:#x}, mem: {:#x}/{:#x}, stack: {:#x}/{:#x}", - mbuff.as_ptr() as u64, - mbuff.len(), - mem.as_ptr() as u64, - mem.len(), - stack.as_ptr() as u64, - stack.len() - ); - Ok(()) -} - -#[cfg(feature = "user")] -fn check_mem( - addr: u64, - len: usize, - access_type: &str, - insn_ptr: usize, - mbuff: &[u8], - mem: &[u8], - stack: &[u8], -) -> Result<(), Error> { - if let Some(addr_end) = addr.checked_add(len as u64) { - if mbuff.as_ptr() as u64 <= addr && addr_end <= mbuff.as_ptr() as u64 + mbuff.len() as u64 { - return Ok(()); - } - if mem.as_ptr() as u64 <= addr && addr_end <= mem.as_ptr() as u64 + mem.len() as u64 { - return Ok(()); - } - if stack.as_ptr() as u64 <= addr && addr_end <= stack.as_ptr() as u64 + stack.len() as u64 { - return Ok(()); - } - } - - Err(Error::new(ErrorKind::Other, format!( - "Error: out of bounds memory {} (insn #{:?}), addr {:#x}, size {:?}\nmbuff: {:#x}/{:#x}, mem: {:#x}/{:#x}, stack: {:#x}/{:#x}", - access_type, insn_ptr, addr, len, - mbuff.as_ptr() as u64, mbuff.len(), - mem.as_ptr() as u64, mem.len(), - stack.as_ptr() as u64, stack.len() - ))) -} - -#[inline] -fn do_jump(insn_ptr: &mut usize, insn: &Insn) { - *insn_ptr = (*insn_ptr as i16 + insn.off) as usize; -} - -#[allow(unknown_lints)] -#[allow(cyclomatic_complexity)] -pub fn execute_program( - prog_: Option<&[u8]>, - mem: &[u8], - mbuff: &[u8], - helpers: &HashMap, -) -> Result { - const U32MAX: u64 = u32::MAX as u64; - const SHIFT_MASK_64: u64 = 0x3f; - - let prog = match prog_ { - Some(prog) => prog, - None => Err(Error::new( - ErrorKind::Other, - "Error: No program set, call prog_set() to load one", - ))?, - }; - let mut stacks = Vec::new(); - let stack = StackFrame::new(); - // R1 points to beginning of memory area, R10 to stack - let mut reg: [u64; 11] = [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - stack.as_ptr() as u64 + stack.len() as u64, - ]; - stacks.push(stack); - if !mbuff.is_empty() { - reg[1] = mbuff.as_ptr() as u64; - } else if !mem.is_empty() { - reg[1] = mem.as_ptr() as u64; - } - let check_mem_load = - |stack: &[u8], addr: u64, len: usize, insn_ptr: usize| -> Result<(), Error> { - check_mem(addr, len, "load", insn_ptr, mbuff, mem, stack) - }; - let check_mem_store = - |stack: &[u8], addr: u64, len: usize, insn_ptr: usize| -> Result<(), Error> { - check_mem(addr, len, "store", insn_ptr, mbuff, mem, stack) - }; - - // Loop on instructions - let mut insn_ptr: usize = 0; - while insn_ptr * ebpf::INSN_SIZE < prog.len() { - let insn = ebpf::get_insn(prog, insn_ptr); - insn_ptr += 1; - let _dst = insn.dst as usize; - let _src = insn.src as usize; - - match insn.opc { - // BPF_LD class - // LD_ABS_* and LD_IND_* are supposed to load pointer to data from metadata buffer. - // Since this pointer is constant, and since we already know it (mem), do not - // bother re-fetching it, just use mem already. - ebpf::LD_ABS_B => { - reg[0] = unsafe { - let x = (mem.as_ptr() as u64 + (insn.imm as u32) as u64) as *const u8; - check_mem_load(stacks.last().unwrap().as_slice(), x as u64, 8, insn_ptr)?; - x.read_unaligned() as u64 - } - } - ebpf::LD_ABS_H => { - reg[0] = unsafe { - let x = (mem.as_ptr() as u64 + (insn.imm as u32) as u64) as *const u16; - check_mem_load(stacks.last().unwrap().as_slice(), x as u64, 8, insn_ptr)?; - x.read_unaligned() as u64 - } - } - ebpf::LD_ABS_W => { - reg[0] = unsafe { - let x = (mem.as_ptr() as u64 + (insn.imm as u32) as u64) as *const u32; - check_mem_load(stacks.last().unwrap().as_slice(), x as u64, 8, insn_ptr)?; - x.read_unaligned() as u64 - } - } - ebpf::LD_ABS_DW => { - log::info!("executing LD_ABS_DW, set reg[{}] to {:#x}", _dst, insn.imm); - reg[0] = unsafe { - let x = (mem.as_ptr() as u64 + (insn.imm as u32) as u64) as *const u64; - check_mem_load(stacks.last().unwrap().as_slice(), x as u64, 8, insn_ptr)?; - x.read_unaligned() - } - } - ebpf::LD_IND_B => { - reg[0] = unsafe { - let x = - (mem.as_ptr() as u64 + reg[_src] + (insn.imm as u32) as u64) as *const u8; - check_mem_load(stacks.last().unwrap().as_slice(), x as u64, 8, insn_ptr)?; - x.read_unaligned() as u64 - } - } - ebpf::LD_IND_H => { - reg[0] = unsafe { - let x = - (mem.as_ptr() as u64 + reg[_src] + (insn.imm as u32) as u64) as *const u16; - check_mem_load(stacks.last().unwrap().as_slice(), x as u64, 8, insn_ptr)?; - x.read_unaligned() as u64 - } - } - ebpf::LD_IND_W => { - reg[0] = unsafe { - let x = - (mem.as_ptr() as u64 + reg[_src] + (insn.imm as u32) as u64) as *const u32; - check_mem_load(stacks.last().unwrap().as_slice(), x as u64, 8, insn_ptr)?; - x.read_unaligned() as u64 - } - } - ebpf::LD_IND_DW => { - reg[0] = unsafe { - let x = - (mem.as_ptr() as u64 + reg[_src] + (insn.imm as u32) as u64) as *const u64; - check_mem_load(stacks.last().unwrap().as_slice(), x as u64, 8, insn_ptr)?; - x.read_unaligned() - } - } - - ebpf::LD_DW_IMM => { - let next_insn = ebpf::get_insn(prog, insn_ptr); - insn_ptr += 1; - // log::warn!( - // "executing LD_DW_IMM, set reg[{}] to {:#x}", - // _dst, - // ((insn.imm as u32) as u64) + ((next_insn.imm as u64) << 32) - // ); - reg[_dst] = ((insn.imm as u32) as u64) + ((next_insn.imm as u64) << 32); - } - - // BPF_LDX class - ebpf::LD_B_REG => { - reg[_dst] = unsafe { - #[allow(clippy::cast_ptr_alignment)] - let x = (reg[_src] as *const u8).offset(insn.off as isize); - check_mem_load(stacks.last().unwrap().as_slice(), x as u64, 1, insn_ptr)?; - x.read_unaligned() as u64 - } - } - ebpf::LD_H_REG => { - reg[_dst] = unsafe { - #[allow(clippy::cast_ptr_alignment)] - let x = (reg[_src] as *const u8).offset(insn.off as isize) as *const u16; - check_mem_load(stacks.last().unwrap().as_slice(), x as u64, 2, insn_ptr)?; - x.read_unaligned() as u64 - } - } - ebpf::LD_W_REG => { - reg[_dst] = unsafe { - #[allow(clippy::cast_ptr_alignment)] - let x = (reg[_src] as *const u8).offset(insn.off as isize) as *const u32; - check_mem_load(stacks.last().unwrap().as_slice(), x as u64, 4, insn_ptr)?; - // log::warn!( - // "executing LD_W_REG, the ptr is REG:{} -> [{:#x}] + {:#x}", - // _src, - // reg[_src], - // insn.off - // ); - x.read_unaligned() as u64 - } - } - ebpf::LD_DW_REG => { - reg[_dst] = unsafe { - #[allow(clippy::cast_ptr_alignment)] - let x = (reg[_src] as *const u8).offset(insn.off as isize) as *const u64; - check_mem_load(stacks.last().unwrap().as_slice(), x as u64, 8, insn_ptr)?; - x.read_unaligned() - } - } - - // BPF_ST class - ebpf::ST_B_IMM => unsafe { - let x = (reg[_dst] as *const u8).offset(insn.off as isize) as *mut u8; - check_mem_store(stacks.last().unwrap().as_slice(), x as u64, 1, insn_ptr)?; - x.write_unaligned(insn.imm as u8); - }, - ebpf::ST_H_IMM => unsafe { - #[allow(clippy::cast_ptr_alignment)] - let x = (reg[_dst] as *const u8).offset(insn.off as isize) as *mut u16; - check_mem_store(stacks.last().unwrap().as_slice(), x as u64, 2, insn_ptr)?; - x.write_unaligned(insn.imm as u16); - }, - ebpf::ST_W_IMM => unsafe { - #[allow(clippy::cast_ptr_alignment)] - let x = (reg[_dst] as *const u8).offset(insn.off as isize) as *mut u32; - check_mem_store(stacks.last().unwrap().as_slice(), x as u64, 4, insn_ptr)?; - x.write_unaligned(insn.imm as u32); - }, - ebpf::ST_DW_IMM => unsafe { - #[allow(clippy::cast_ptr_alignment)] - let x = (reg[_dst] as *const u8).offset(insn.off as isize) as *mut u64; - check_mem_store(stacks.last().unwrap().as_slice(), x as u64, 8, insn_ptr)?; - x.write_unaligned(insn.imm as u64); - }, - - // BPF_STX class - ebpf::ST_B_REG => unsafe { - let x = (reg[_dst] as *const u8).offset(insn.off as isize) as *mut u8; - check_mem_store(stacks.last().unwrap().as_slice(), x as u64, 1, insn_ptr)?; - x.write_unaligned(reg[_src] as u8); - }, - ebpf::ST_H_REG => unsafe { - #[allow(clippy::cast_ptr_alignment)] - let x = (reg[_dst] as *const u8).offset(insn.off as isize) as *mut u16; - check_mem_store(stacks.last().unwrap().as_slice(), x as u64, 2, insn_ptr)?; - x.write_unaligned(reg[_src] as u16); - }, - ebpf::ST_W_REG => unsafe { - #[allow(clippy::cast_ptr_alignment)] - let x = (reg[_dst] as *const u8).offset(insn.off as isize) as *mut u32; - check_mem_store(stacks.last().unwrap().as_slice(), x as u64, 4, insn_ptr)?; - x.write_unaligned(reg[_src] as u32); - }, - ebpf::ST_DW_REG => unsafe { - #[allow(clippy::cast_ptr_alignment)] - let x = (reg[_dst] as *const u8).offset(insn.off as isize) as *mut u64; - check_mem_store(stacks.last().unwrap().as_slice(), x as u64, 8, insn_ptr)?; - x.write_unaligned(reg[_src]); - }, - ebpf::ST_W_XADD => unimplemented!(), - ebpf::ST_DW_XADD => unimplemented!(), - - // BPF_ALU class - // TODO Check how overflow works in kernel. Should we &= U32MAX all src register value - // before we do the operation? - // Cf ((0x11 << 32) - (0x1 << 32)) as u32 VS ((0x11 << 32) as u32 - (0x1 << 32) as u32 - ebpf::ADD32_IMM => reg[_dst] = (reg[_dst] as i32).wrapping_add(insn.imm) as u64, //((reg[_dst] & U32MAX) + insn.imm as u64) & U32MAX, - ebpf::ADD32_REG => reg[_dst] = (reg[_dst] as i32).wrapping_add(reg[_src] as i32) as u64, //((reg[_dst] & U32MAX) + (reg[_src] & U32MAX)) & U32MAX, - ebpf::SUB32_IMM => reg[_dst] = (reg[_dst] as i32).wrapping_sub(insn.imm) as u64, - ebpf::SUB32_REG => reg[_dst] = (reg[_dst] as i32).wrapping_sub(reg[_src] as i32) as u64, - ebpf::MUL32_IMM => reg[_dst] = (reg[_dst] as i32).wrapping_mul(insn.imm) as u64, - ebpf::MUL32_REG => reg[_dst] = (reg[_dst] as i32).wrapping_mul(reg[_src] as i32) as u64, - ebpf::DIV32_IMM if insn.imm as u32 == 0 => reg[_dst] = 0, - ebpf::DIV32_IMM => reg[_dst] = (reg[_dst] as u32 / insn.imm as u32) as u64, - ebpf::DIV32_REG if reg[_src] as u32 == 0 => reg[_dst] = 0, - ebpf::DIV32_REG => reg[_dst] = (reg[_dst] as u32 / reg[_src] as u32) as u64, - ebpf::OR32_IMM => reg[_dst] = (reg[_dst] as u32 | insn.imm as u32) as u64, - ebpf::OR32_REG => reg[_dst] = (reg[_dst] as u32 | reg[_src] as u32) as u64, - ebpf::AND32_IMM => reg[_dst] = (reg[_dst] as u32 & insn.imm as u32) as u64, - ebpf::AND32_REG => reg[_dst] = (reg[_dst] as u32 & reg[_src] as u32) as u64, - // As for the 64-bit version, we should mask the number of bits to shift with - // 0x1f, but .wrappping_shr() already takes care of it for us. - ebpf::LSH32_IMM => reg[_dst] = (reg[_dst] as u32).wrapping_shl(insn.imm as u32) as u64, - ebpf::LSH32_REG => reg[_dst] = (reg[_dst] as u32).wrapping_shl(reg[_src] as u32) as u64, - ebpf::RSH32_IMM => reg[_dst] = (reg[_dst] as u32).wrapping_shr(insn.imm as u32) as u64, - ebpf::RSH32_REG => reg[_dst] = (reg[_dst] as u32).wrapping_shr(reg[_src] as u32) as u64, - ebpf::NEG32 => { - reg[_dst] = (reg[_dst] as i32).wrapping_neg() as u64; - reg[_dst] &= U32MAX; - } - ebpf::MOD32_IMM if insn.imm as u32 == 0 => (), - ebpf::MOD32_IMM => reg[_dst] = (reg[_dst] as u32 % insn.imm as u32) as u64, - ebpf::MOD32_REG if reg[_src] as u32 == 0 => (), - ebpf::MOD32_REG => reg[_dst] = (reg[_dst] as u32 % reg[_src] as u32) as u64, - ebpf::XOR32_IMM => reg[_dst] = (reg[_dst] as u32 ^ insn.imm as u32) as u64, - ebpf::XOR32_REG => reg[_dst] = (reg[_dst] as u32 ^ reg[_src] as u32) as u64, - ebpf::MOV32_IMM => reg[_dst] = insn.imm as u32 as u64, - ebpf::MOV32_REG => reg[_dst] = (reg[_src] as u32) as u64, - // As for the 64-bit version, we should mask the number of bits to shift with - // 0x1f, but .wrappping_shr() already takes care of it for us. - ebpf::ARSH32_IMM => { - reg[_dst] = (reg[_dst] as i32).wrapping_shr(insn.imm as u32) as u64; - reg[_dst] &= U32MAX; - } - ebpf::ARSH32_REG => { - reg[_dst] = (reg[_dst] as i32).wrapping_shr(reg[_src] as u32) as u64; - reg[_dst] &= U32MAX; - } - ebpf::LE => { - reg[_dst] = match insn.imm { - 16 => (reg[_dst] as u16).to_le() as u64, - 32 => (reg[_dst] as u32).to_le() as u64, - 64 => reg[_dst].to_le(), - _ => unreachable!(), - }; - } - ebpf::BE => { - reg[_dst] = match insn.imm { - 16 => (reg[_dst] as u16).to_be() as u64, - 32 => (reg[_dst] as u32).to_be() as u64, - 64 => reg[_dst].to_be(), - _ => unreachable!(), - }; - } - - // BPF_ALU64 class - ebpf::ADD64_IMM => reg[_dst] = reg[_dst].wrapping_add(insn.imm as u64), - ebpf::ADD64_REG => reg[_dst] = reg[_dst].wrapping_add(reg[_src]), - ebpf::SUB64_IMM => reg[_dst] = reg[_dst].wrapping_sub(insn.imm as u64), - ebpf::SUB64_REG => reg[_dst] = reg[_dst].wrapping_sub(reg[_src]), - ebpf::MUL64_IMM => reg[_dst] = reg[_dst].wrapping_mul(insn.imm as u64), - ebpf::MUL64_REG => reg[_dst] = reg[_dst].wrapping_mul(reg[_src]), - ebpf::DIV64_IMM if insn.imm == 0 => reg[_dst] = 0, - ebpf::DIV64_IMM => reg[_dst] /= insn.imm as u64, - ebpf::DIV64_REG if reg[_src] == 0 => reg[_dst] = 0, - ebpf::DIV64_REG => reg[_dst] /= reg[_src], - ebpf::OR64_IMM => reg[_dst] |= insn.imm as u64, - ebpf::OR64_REG => reg[_dst] |= reg[_src], - ebpf::AND64_IMM => reg[_dst] &= insn.imm as u64, - ebpf::AND64_REG => reg[_dst] &= reg[_src], - ebpf::LSH64_IMM => reg[_dst] <<= insn.imm as u64 & SHIFT_MASK_64, - ebpf::LSH64_REG => reg[_dst] <<= reg[_src] & SHIFT_MASK_64, - ebpf::RSH64_IMM => reg[_dst] >>= insn.imm as u64 & SHIFT_MASK_64, - ebpf::RSH64_REG => reg[_dst] >>= reg[_src] & SHIFT_MASK_64, - ebpf::NEG64 => reg[_dst] = -(reg[_dst] as i64) as u64, - ebpf::MOD64_IMM if insn.imm == 0 => (), - ebpf::MOD64_IMM => reg[_dst] %= insn.imm as u64, - ebpf::MOD64_REG if reg[_src] == 0 => (), - ebpf::MOD64_REG => reg[_dst] %= reg[_src], - ebpf::XOR64_IMM => reg[_dst] ^= insn.imm as u64, - ebpf::XOR64_REG => reg[_dst] ^= reg[_src], - ebpf::MOV64_IMM => reg[_dst] = insn.imm as u64, - ebpf::MOV64_REG => reg[_dst] = reg[_src], - ebpf::ARSH64_IMM => { - reg[_dst] = (reg[_dst] as i64 >> (insn.imm as u64 & SHIFT_MASK_64)) as u64 - } - ebpf::ARSH64_REG => { - reg[_dst] = (reg[_dst] as i64 >> (reg[_src] as u64 & SHIFT_MASK_64)) as u64 - } - - // BPF_JMP class - // TODO: check this actually works as expected for signed / unsigned ops - ebpf::JA => do_jump(&mut insn_ptr, &insn), - ebpf::JEQ_IMM => { - if reg[_dst] == insn.imm as u64 { - do_jump(&mut insn_ptr, &insn); - } - } - ebpf::JEQ_REG => { - if reg[_dst] == reg[_src] { - do_jump(&mut insn_ptr, &insn); - } - } - ebpf::JGT_IMM => { - if reg[_dst] > insn.imm as u64 { - do_jump(&mut insn_ptr, &insn); - } - } - ebpf::JGT_REG => { - if reg[_dst] > reg[_src] { - do_jump(&mut insn_ptr, &insn); - } - } - ebpf::JGE_IMM => { - if reg[_dst] >= insn.imm as u64 { - do_jump(&mut insn_ptr, &insn); - } - } - ebpf::JGE_REG => { - if reg[_dst] >= reg[_src] { - do_jump(&mut insn_ptr, &insn); - } - } - ebpf::JLT_IMM => { - if reg[_dst] < insn.imm as u64 { - do_jump(&mut insn_ptr, &insn); - } - } - ebpf::JLT_REG => { - if reg[_dst] < reg[_src] { - do_jump(&mut insn_ptr, &insn); - } - } - ebpf::JLE_IMM => { - if reg[_dst] <= insn.imm as u64 { - do_jump(&mut insn_ptr, &insn); - } - } - ebpf::JLE_REG => { - if reg[_dst] <= reg[_src] { - do_jump(&mut insn_ptr, &insn); - } - } - ebpf::JSET_IMM => { - if reg[_dst] & insn.imm as u64 != 0 { - do_jump(&mut insn_ptr, &insn); - } - } - ebpf::JSET_REG => { - if reg[_dst] & reg[_src] != 0 { - do_jump(&mut insn_ptr, &insn); - } - } - ebpf::JNE_IMM => { - if reg[_dst] != insn.imm as u64 { - do_jump(&mut insn_ptr, &insn); - } - } - ebpf::JNE_REG => { - if reg[_dst] != reg[_src] { - do_jump(&mut insn_ptr, &insn); - } - } - ebpf::JSGT_IMM => { - if reg[_dst] as i64 > insn.imm as i64 { - do_jump(&mut insn_ptr, &insn); - } - } - ebpf::JSGT_REG => { - if reg[_dst] as i64 > reg[_src] as i64 { - do_jump(&mut insn_ptr, &insn); - } - } - ebpf::JSGE_IMM => { - if reg[_dst] as i64 >= insn.imm as i64 { - do_jump(&mut insn_ptr, &insn); - } - } - ebpf::JSGE_REG => { - if reg[_dst] as i64 >= reg[_src] as i64 { - do_jump(&mut insn_ptr, &insn); - } - } - ebpf::JSLT_IMM => { - if (reg[_dst] as i64) < insn.imm as i64 { - do_jump(&mut insn_ptr, &insn); - } - } - ebpf::JSLT_REG => { - if (reg[_dst] as i64) < reg[_src] as i64 { - do_jump(&mut insn_ptr, &insn); - } - } - ebpf::JSLE_IMM => { - if reg[_dst] as i64 <= insn.imm as i64 { - do_jump(&mut insn_ptr, &insn); - } - } - ebpf::JSLE_REG => { - if reg[_dst] as i64 <= reg[_src] as i64 { - do_jump(&mut insn_ptr, &insn); - } - } - - // BPF_JMP32 class - ebpf::JEQ_IMM32 => { - if reg[_dst] as u32 == insn.imm as u32 { - do_jump(&mut insn_ptr, &insn); - } - } - ebpf::JEQ_REG32 => { - if reg[_dst] as u32 == reg[_src] as u32 { - do_jump(&mut insn_ptr, &insn); - } - } - ebpf::JGT_IMM32 => { - if reg[_dst] as u32 > insn.imm as u32 { - do_jump(&mut insn_ptr, &insn); - } - } - ebpf::JGT_REG32 => { - if reg[_dst] as u32 > reg[_src] as u32 { - do_jump(&mut insn_ptr, &insn); - } - } - ebpf::JGE_IMM32 => { - if reg[_dst] as u32 >= insn.imm as u32 { - do_jump(&mut insn_ptr, &insn); - } - } - ebpf::JGE_REG32 => { - if reg[_dst] as u32 >= reg[_src] as u32 { - do_jump(&mut insn_ptr, &insn); - } - } - ebpf::JLT_IMM32 => { - if (reg[_dst] as u32) < insn.imm as u32 { - do_jump(&mut insn_ptr, &insn); - } - } - ebpf::JLT_REG32 => { - if (reg[_dst] as u32) < reg[_src] as u32 { - do_jump(&mut insn_ptr, &insn); - } - } - ebpf::JLE_IMM32 => { - if reg[_dst] as u32 <= insn.imm as u32 { - do_jump(&mut insn_ptr, &insn); - } - } - ebpf::JLE_REG32 => { - if reg[_dst] as u32 <= reg[_src] as u32 { - do_jump(&mut insn_ptr, &insn); - } - } - ebpf::JSET_IMM32 => { - if reg[_dst] as u32 & insn.imm as u32 != 0 { - do_jump(&mut insn_ptr, &insn); - } - } - ebpf::JSET_REG32 => { - if reg[_dst] as u32 & reg[_src] as u32 != 0 { - do_jump(&mut insn_ptr, &insn); - } - } - ebpf::JNE_IMM32 => { - if reg[_dst] as u32 != insn.imm as u32 { - do_jump(&mut insn_ptr, &insn); - } - } - ebpf::JNE_REG32 => { - if reg[_dst] as u32 != reg[_src] as u32 { - do_jump(&mut insn_ptr, &insn); - } - } - ebpf::JSGT_IMM32 => { - if reg[_dst] as i32 > insn.imm { - do_jump(&mut insn_ptr, &insn); - } - } - ebpf::JSGT_REG32 => { - if reg[_dst] as i32 > reg[_src] as i32 { - do_jump(&mut insn_ptr, &insn); - } - } - ebpf::JSGE_IMM32 => { - if reg[_dst] as i32 >= insn.imm { - do_jump(&mut insn_ptr, &insn); - } - } - ebpf::JSGE_REG32 => { - if reg[_dst] as i32 >= reg[_src] as i32 { - do_jump(&mut insn_ptr, &insn); - } - } - ebpf::JSLT_IMM32 => { - if (reg[_dst] as i32) < insn.imm { - do_jump(&mut insn_ptr, &insn); - } - } - ebpf::JSLT_REG32 => { - if (reg[_dst] as i32) < reg[_src] as i32 { - do_jump(&mut insn_ptr, &insn); - } - } - ebpf::JSLE_IMM32 => { - if reg[_dst] as i32 <= insn.imm { - do_jump(&mut insn_ptr, &insn); - } - } - ebpf::JSLE_REG32 => { - if reg[_dst] as i32 <= reg[_src] as i32 { - do_jump(&mut insn_ptr, &insn); - } - } - - // Do not delegate the check to the verifier, since registered functions can be - // changed after the program has been verified. - ebpf::CALL => { - // See https://www.kernel.org/doc/html/latest/bpf/standardization/instruction-set.html#id16 - let src_reg = _src; - let call_func_res = match src_reg { - 0 => { - // Handle call by address to external function. - if let Some(function) = helpers.get(&(insn.imm as u32)) { - reg[0] = function(reg[1], reg[2], reg[3], reg[4], reg[5]); - Ok(()) - }else { - Err(format!( - "Error: unknown helper function (id: {:#x}) [{}], (instruction #{})", - insn.imm as u32,BPF_FUNC_MAPPER[insn.imm as usize],insn_ptr - )) - } - } - 1 => { - // bpf to bpf call - // The function is in the same program, so we can just jump to the address - if stacks.len() >= ebpf::RBPF_MAX_CALL_DEPTH{ - Err(format!( - "Error: bpf to bpf call stack limit reached (instruction #{}) max depth: {}", - insn_ptr, ebpf::RBPF_MAX_CALL_DEPTH - )) - }else { - let mut pre_stack = stacks.last_mut().unwrap(); - // Save the callee saved registers - pre_stack.save_registers(®[6..=9]); - // Save the return address - pre_stack.save_return_address(insn_ptr as u64); - // save the stack pointer - pre_stack.save_sp(reg[10]); - let mut stack = StackFrame::new(); - log::trace!("BPF TO BPF CALL: new pc: {} + {} = {}",insn_ptr ,insn.imm,insn_ptr + insn.imm as usize); - reg[10] = stack.as_ptr() as u64 + stack.len() as u64; - stacks.push(stack); - insn_ptr += insn.imm as usize; - Ok(()) - } - } - _ =>{ - Err(format!( - "Error: the function call type (id: {:#x}) [{}], (instruction #{}) not supported", - insn.imm as u32,BPF_FUNC_MAPPER[insn.imm as usize],insn_ptr - )) - } - }; - if let Err(e) = call_func_res { - Err(Error::new(ErrorKind::Other, e))?; - } - } - ebpf::TAIL_CALL => unimplemented!(), - ebpf::EXIT => { - if stacks.len() == 1 { - return Ok(reg[0]); - } else { - // Pop the stack - stacks.pop(); - let stack = stacks.last().unwrap(); - // Restore the callee saved registers - reg[6..=9].copy_from_slice(&stack.get_registers()); - // Restore the return address - insn_ptr = stack.get_return_address() as usize; - // Restore the stack pointer - reg[10] = stack.get_sp(); - log::trace!("EXIT: new pc: {}", insn_ptr); - } - } - - _ => unreachable!(), - } - } - - unreachable!() -} diff --git a/kernel/crates/rbpf/src/jit.rs b/kernel/crates/rbpf/src/jit.rs deleted file mode 100644 index df509ce0..00000000 --- a/kernel/crates/rbpf/src/jit.rs +++ /dev/null @@ -1,1054 +0,0 @@ -// SPDX-License-Identifier: (Apache-2.0 OR MIT) -// Derived from uBPF -// Copyright 2015 Big Switch Networks, Inc -// (uBPF: JIT algorithm, originally in C) -// Copyright 2016 6WIND S.A. -// (Translation to Rust, MetaBuff addition) - -use std::{ - fmt::{Error as FormatterError, Formatter}, - io::{Error, ErrorKind}, - mem, - ops::{Index, IndexMut}, -}; - -use crate::{ebpf, HashMap}; - -extern crate libc; - -type MachineCode = unsafe fn(*mut u8, usize, *mut u8, usize, usize, usize) -> u64; - -const PAGE_SIZE: usize = 4096; -// TODO: check how long the page must be to be sure to support an eBPF program of maximum possible -// length -const NUM_PAGES: usize = 1; - -// Special values for target_pc in struct Jump -const TARGET_OFFSET: isize = ebpf::PROG_MAX_INSNS as isize; -const TARGET_PC_EXIT: isize = TARGET_OFFSET + 1; - -#[derive(Copy, Clone)] -enum OperandSize { - S8 = 8, - S16 = 16, - S32 = 32, - S64 = 64, -} - -// Registers -const RAX: u8 = 0; -const RCX: u8 = 1; -const RDX: u8 = 2; -const RBX: u8 = 3; -const RSP: u8 = 4; -const RBP: u8 = 5; -const RSI: u8 = 6; -const RDI: u8 = 7; -const R8: u8 = 8; -const R9: u8 = 9; -const R10: u8 = 10; -const R11: u8 = 11; -//const R12: u8 = 12; -const R13: u8 = 13; -const R14: u8 = 14; -const R15: u8 = 15; - -const REGISTER_MAP_SIZE: usize = 11; -const REGISTER_MAP: [u8; REGISTER_MAP_SIZE] = [ - RAX, // 0 return value - RDI, // 1 arg 1 - RSI, // 2 arg 2 - RDX, // 3 arg 3 - R9, // 4 arg 4 - R8, // 5 arg 5 - RBX, // 6 callee-saved - R13, // 7 callee-saved - R14, // 8 callee-saved - R15, // 9 callee-saved - RBP, // 10 stack pointer - // R10 and R11 are used to compute store a constant pointer to mem and to compute offset for - // LD_ABS_* and LD_IND_* operations, so they are not mapped to any eBPF register. -]; - -// Return the x86 register for the given eBPF register -fn map_register(r: u8) -> u8 { - assert!(r < REGISTER_MAP_SIZE as u8); - REGISTER_MAP[(r % REGISTER_MAP_SIZE as u8) as usize] -} - -macro_rules! emit_bytes { - ( $mem:ident, $data:tt, $t:ty ) => {{ - let size = mem::size_of::<$t>() as usize; - assert!($mem.offset + size <= $mem.contents.len()); - unsafe { - let mut ptr = $mem.contents.as_ptr().add($mem.offset) as *mut $t; - ptr.write_unaligned($data); - } - $mem.offset += size; - }}; -} - -#[derive(Debug)] -struct Jump { - offset_loc: usize, - target_pc: isize, -} - -#[derive(Debug)] -struct JitCompiler { - pc_locs: Vec, - special_targets: HashMap, - jumps: Vec, -} - -impl JitCompiler { - fn new() -> JitCompiler { - JitCompiler { - pc_locs: vec![], - jumps: vec![], - special_targets: HashMap::new(), - } - } - - fn emit1(&self, mem: &mut JitMemory, data: u8) { - emit_bytes!(mem, data, u8); - } - - fn emit2(&self, mem: &mut JitMemory, data: u16) { - emit_bytes!(mem, data, u16); - } - - fn emit4(&self, mem: &mut JitMemory, data: u32) { - emit_bytes!(mem, data, u32); - } - - fn emit8(&self, mem: &mut JitMemory, data: u64) { - emit_bytes!(mem, data, u64); - } - - fn emit_modrm(&self, mem: &mut JitMemory, modrm: u8, r: u8, m: u8) { - assert_eq!((modrm | 0xc0), 0xc0); - self.emit1(mem, (modrm & 0xc0) | ((r & 0b111) << 3) | (m & 0b111)); - } - - fn emit_modrm_reg2reg(&self, mem: &mut JitMemory, r: u8, m: u8) { - self.emit_modrm(mem, 0xc0, r, m); - } - - fn emit_modrm_and_displacement(&self, mem: &mut JitMemory, r: u8, m: u8, d: i32) { - if d == 0 && (m & 0b111) != RBP { - self.emit_modrm(mem, 0x00, r, m); - } else if (-128..=127).contains(&d) { - self.emit_modrm(mem, 0x40, r, m); - self.emit1(mem, d as u8); - } else { - self.emit_modrm(mem, 0x80, r, m); - self.emit4(mem, d as u32); - } - } - - fn basix_rex_would_set_bits(&self, w: u8, src: u8, dst: u8) -> bool { - w != 0 || (src & 0b1000) != 0 || (dst & 0b1000) != 0 - } - - fn emit_rex(&self, mem: &mut JitMemory, w: u8, r: u8, x: u8, b: u8) { - assert_eq!((w | 1), 1); - assert_eq!((r | 1), 1); - assert_eq!((x | 1), 1); - assert_eq!((b | 1), 1); - self.emit1(mem, 0x40 | (w << 3) | (r << 2) | (x << 1) | b); - } - - // Emits a REX prefix with the top bit of src and dst. - // Skipped if no bits would be set. - fn emit_basic_rex(&self, mem: &mut JitMemory, w: u8, src: u8, dst: u8) { - if self.basix_rex_would_set_bits(w, src, dst) { - let is_masked = |val, mask| match val & mask { - 0 => 0, - _ => 1, - }; - self.emit_rex(mem, w, is_masked(src, 8), 0, is_masked(dst, 8)); - } - } - - fn emit_push(&self, mem: &mut JitMemory, r: u8) { - self.emit_basic_rex(mem, 0, 0, r); - self.emit1(mem, 0x50 | (r & 0b111)); - } - - fn emit_pop(&self, mem: &mut JitMemory, r: u8) { - self.emit_basic_rex(mem, 0, 0, r); - self.emit1(mem, 0x58 | (r & 0b111)); - } - - // REX prefix and ModRM byte - // We use the MR encoding when there is a choice - // 'src' is often used as an opcode extension - fn emit_alu32(&self, mem: &mut JitMemory, op: u8, src: u8, dst: u8) { - self.emit_basic_rex(mem, 0, src, dst); - self.emit1(mem, op); - self.emit_modrm_reg2reg(mem, src, dst); - } - - // REX prefix, ModRM byte, and 32-bit immediate - fn emit_alu32_imm32(&self, mem: &mut JitMemory, op: u8, src: u8, dst: u8, imm: i32) { - self.emit_alu32(mem, op, src, dst); - self.emit4(mem, imm as u32); - } - - // REX prefix, ModRM byte, and 8-bit immediate - fn emit_alu32_imm8(&self, mem: &mut JitMemory, op: u8, src: u8, dst: u8, imm: i8) { - self.emit_alu32(mem, op, src, dst); - self.emit1(mem, imm as u8); - } - - // REX.W prefix and ModRM byte - // We use the MR encoding when there is a choice - // 'src' is often used as an opcode extension - fn emit_alu64(&self, mem: &mut JitMemory, op: u8, src: u8, dst: u8) { - self.emit_basic_rex(mem, 1, src, dst); - self.emit1(mem, op); - self.emit_modrm_reg2reg(mem, src, dst); - } - - // REX.W prefix, ModRM byte, and 32-bit immediate - fn emit_alu64_imm32(&self, mem: &mut JitMemory, op: u8, src: u8, dst: u8, imm: i32) { - self.emit_alu64(mem, op, src, dst); - self.emit4(mem, imm as u32); - } - - // REX.W prefix, ModRM byte, and 8-bit immediate - fn emit_alu64_imm8(&self, mem: &mut JitMemory, op: u8, src: u8, dst: u8, imm: i8) { - self.emit_alu64(mem, op, src, dst); - self.emit1(mem, imm as u8); - } - - // Register to register mov - fn emit_mov(&self, mem: &mut JitMemory, src: u8, dst: u8) { - self.emit_alu64(mem, 0x89, src, dst); - } - - fn emit_cmp_imm32(&self, mem: &mut JitMemory, dst: u8, imm: i32) { - self.emit_alu64_imm32(mem, 0x81, 7, dst, imm); - } - - fn emit_cmp(&self, mem: &mut JitMemory, src: u8, dst: u8) { - self.emit_alu64(mem, 0x39, src, dst); - } - - fn emit_cmp32_imm32(&self, mem: &mut JitMemory, dst: u8, imm: i32) { - self.emit_alu32_imm32(mem, 0x81, 7, dst, imm); - } - - fn emit_cmp32(&self, mem: &mut JitMemory, src: u8, dst: u8) { - self.emit_alu32(mem, 0x39, src, dst); - } - - // Load [src + offset] into dst - fn emit_load(&self, mem: &mut JitMemory, size: OperandSize, src: u8, dst: u8, offset: i32) { - let data = match size { - OperandSize::S64 => 1, - _ => 0, - }; - self.emit_basic_rex(mem, data, dst, src); - - match size { - OperandSize::S8 => { - // movzx - self.emit1(mem, 0x0f); - self.emit1(mem, 0xb6); - } - OperandSize::S16 => { - // movzx - self.emit1(mem, 0x0f); - self.emit1(mem, 0xb7); - } - OperandSize::S32 | OperandSize::S64 => { - // mov - self.emit1(mem, 0x8b); - } - } - - self.emit_modrm_and_displacement(mem, dst, src, offset); - } - - // Load sign-extended immediate into register - fn emit_load_imm(&self, mem: &mut JitMemory, dst: u8, imm: i64) { - if imm >= i32::MIN as i64 && imm <= i32::MAX as i64 { - self.emit_alu64_imm32(mem, 0xc7, 0, dst, imm as i32); - } else { - // movabs $imm,dst - self.emit_basic_rex(mem, 1, 0, dst); - self.emit1(mem, 0xb8 | (dst & 0b111)); - self.emit8(mem, imm as u64); - } - } - - // Store register src to [dst + offset] - fn emit_store(&self, mem: &mut JitMemory, size: OperandSize, src: u8, dst: u8, offset: i32) { - match size { - OperandSize::S16 => self.emit1(mem, 0x66), // 16-bit override - _ => {} - }; - let (is_s8, is_u64, rexw) = match size { - OperandSize::S8 => (true, false, 0), - OperandSize::S64 => (false, true, 1), - _ => (false, false, 0), - }; - if is_u64 || (src & 0b1000) != 0 || (dst & 0b1000) != 0 || is_s8 { - let is_masked = |val, mask| match val & mask { - 0 => 0, - _ => 1, - }; - self.emit_rex(mem, rexw, is_masked(src, 8), 0, is_masked(dst, 8)); - } - match size { - OperandSize::S8 => self.emit1(mem, 0x88), - _ => self.emit1(mem, 0x89), - }; - self.emit_modrm_and_displacement(mem, src, dst, offset); - } - - // Store immediate to [dst + offset] - fn emit_store_imm32( - &self, - mem: &mut JitMemory, - size: OperandSize, - dst: u8, - offset: i32, - imm: i32, - ) { - match size { - OperandSize::S16 => self.emit1(mem, 0x66), // 16-bit override - _ => {} - }; - match size { - OperandSize::S64 => self.emit_basic_rex(mem, 1, 0, dst), - _ => self.emit_basic_rex(mem, 0, 0, dst), - }; - match size { - OperandSize::S8 => self.emit1(mem, 0xc6), - _ => self.emit1(mem, 0xc7), - }; - self.emit_modrm_and_displacement(mem, 0, dst, offset); - match size { - OperandSize::S8 => self.emit1(mem, imm as u8), - OperandSize::S16 => self.emit2(mem, imm as u16), - _ => self.emit4(mem, imm as u32), - }; - } - - fn emit_direct_jcc(&self, mem: &mut JitMemory, code: u8, offset: u32) { - self.emit1(mem, 0x0f); - self.emit1(mem, code); - emit_bytes!(mem, offset, u32); - } - - fn emit_call(&self, mem: &mut JitMemory, target: usize) { - // TODO use direct call when possible - self.emit_load_imm(mem, RAX, target as i64); - // callq *%rax - self.emit1(mem, 0xff); - self.emit1(mem, 0xd0); - } - - fn emit_jump_offset(&mut self, mem: &mut JitMemory, target_pc: isize) { - let jump = Jump { - offset_loc: mem.offset, - target_pc, - }; - self.jumps.push(jump); - self.emit4(mem, 0); - } - - fn emit_jcc(&mut self, mem: &mut JitMemory, code: u8, target_pc: isize) { - self.emit1(mem, 0x0f); - self.emit1(mem, code); - self.emit_jump_offset(mem, target_pc); - } - - fn emit_jmp(&mut self, mem: &mut JitMemory, target_pc: isize) { - self.emit1(mem, 0xe9); - self.emit_jump_offset(mem, target_pc); - } - - fn set_anchor(&mut self, mem: &mut JitMemory, target: isize) { - self.special_targets.insert(target, mem.offset); - } - - fn emit_muldivmod( - &mut self, - mem: &mut JitMemory, - pc: u16, - opc: u8, - src: u8, - dst: u8, - imm: i32, - ) { - let mul = (opc & ebpf::BPF_ALU_OP_MASK) == (ebpf::MUL32_IMM & ebpf::BPF_ALU_OP_MASK); - let div = (opc & ebpf::BPF_ALU_OP_MASK) == (ebpf::DIV32_IMM & ebpf::BPF_ALU_OP_MASK); - let modrm = (opc & ebpf::BPF_ALU_OP_MASK) == (ebpf::MOD32_IMM & ebpf::BPF_ALU_OP_MASK); - let is64 = (opc & ebpf::BPF_CLS_MASK) == ebpf::BPF_ALU64; - let is_reg = (opc & ebpf::BPF_X) == ebpf::BPF_X; - - if (div || mul) && !is_reg && imm == 0 { - // Division by zero returns 0 - // Set register to 0: xor with itself - self.emit_alu32(mem, 0x31, dst, dst); - return; - } - if modrm && !is_reg && imm == 0 { - // Modulo remainder of division by zero keeps destination register unchanged - return; - } - if (div || modrm) && is_reg { - self.emit_load_imm(mem, RCX, pc as i64); - - // test src,src - if is64 { - self.emit_alu64(mem, 0x85, src, src); - } else { - self.emit_alu32(mem, 0x85, src, src); - } - - if div { - // No division by 0: skip next instructions - // Jump offset: emit_alu32 adds 2 to 3 bytes, emit_jmp adds 5 - let offset = match self.basix_rex_would_set_bits(0, dst, dst) { - true => 3 + 5, - false => 2 + 5, - }; - self.emit_direct_jcc(mem, 0x85, offset); - // Division by 0: set dst to 0 then go to next instruction - // Set register to 0: xor with itself - self.emit_alu32(mem, 0x31, dst, dst); - self.emit_jmp(mem, (pc + 1) as isize); - } - if modrm { - // Modulo by zero: keep destination register unchanged - self.emit_jcc(mem, 0x84, (pc + 1) as isize); - } - } - - if dst != RAX { - self.emit_push(mem, RAX); - } - if dst != RDX { - self.emit_push(mem, RDX); - } - if imm != 0 { - self.emit_load_imm(mem, RCX, imm as i64); - } else { - self.emit_mov(mem, src, RCX); - } - - self.emit_mov(mem, dst, RAX); - - if div || modrm { - // Set register to 0: xor %edx,%edx - self.emit_alu32(mem, 0x31, RDX, RDX); - } - - if is64 { - self.emit_rex(mem, 1, 0, 0, 0); - } - - // mul %ecx or div %ecx - self.emit_alu32(mem, 0xf7, if mul { 4 } else { 6 }, RCX); - - if dst != RDX { - if modrm { - self.emit_mov(mem, RDX, dst); - } - self.emit_pop(mem, RDX); - } - if dst != RAX { - if div || mul { - self.emit_mov(mem, RAX, dst); - } - self.emit_pop(mem, RAX); - } - } - - fn jit_compile( - &mut self, - mem: &mut JitMemory, - prog: &[u8], - use_mbuff: bool, - update_data_ptr: bool, - helpers: &HashMap, - ) -> Result<(), Error> { - self.emit_push(mem, RBP); - self.emit_push(mem, RBX); - self.emit_push(mem, R13); - self.emit_push(mem, R14); - self.emit_push(mem, R15); - - // RDI: mbuff - // RSI: mbuff_len - // RDX: mem - // RCX: mem_len - // R8: mem_offset - // R9: mem_end_offset - - // Save mem pointer for use with LD_ABS_* and LD_IND_* instructions - self.emit_mov(mem, RDX, R10); - - match (use_mbuff, update_data_ptr) { - (false, _) => { - // We do not use any mbuff. Move mem pointer into register 1. - if map_register(1) != RDX { - self.emit_mov(mem, RDX, map_register(1)); - } - } - (true, false) => { - // We use a mbuff already pointing to mem and mem_end: move it to register 1. - if map_register(1) != RDI { - self.emit_mov(mem, RDI, map_register(1)); - } - } - (true, true) => { - // We have a fixed (simulated) mbuff: update mem and mem_end offset values in it. - // Store mem at mbuff + mem_offset. Trash R8. - self.emit_alu64(mem, 0x01, RDI, R8); // add mbuff to mem_offset in R8 - self.emit_store(mem, OperandSize::S64, RDX, R8, 0); // set mem at mbuff + mem_offset - // Store mem_end at mbuff + mem_end_offset. Trash R9. - self.emit_load(mem, OperandSize::S64, RDX, R8, 0); // load mem into R8 - self.emit_alu64(mem, 0x01, RCX, R8); // add mem_len to mem (= mem_end) - self.emit_alu64(mem, 0x01, RDI, R9); // add mbuff to mem_end_offset - self.emit_store(mem, OperandSize::S64, R8, R9, 0); // store mem_end - - // Move rdi into register 1 - if map_register(1) != RDI { - self.emit_mov(mem, RDI, map_register(1)); - } - } - } - - // Copy stack pointer to R10 - self.emit_mov(mem, RSP, map_register(10)); - - // Allocate stack space - self.emit_alu64_imm32(mem, 0x81, 5, RSP, ebpf::STACK_SIZE as i32); - - self.pc_locs = vec![0; prog.len() / ebpf::INSN_SIZE + 1]; - - let mut insn_ptr: usize = 0; - while insn_ptr * ebpf::INSN_SIZE < prog.len() { - let insn = ebpf::get_insn(prog, insn_ptr); - - self.pc_locs[insn_ptr] = mem.offset; - - let dst = map_register(insn.dst); - let src = map_register(insn.src); - let target_pc = insn_ptr as isize + insn.off as isize + 1; - - match insn.opc { - // BPF_LD class - // R10 is a constant pointer to mem. - ebpf::LD_ABS_B => self.emit_load(mem, OperandSize::S8, R10, RAX, insn.imm), - ebpf::LD_ABS_H => self.emit_load(mem, OperandSize::S16, R10, RAX, insn.imm), - ebpf::LD_ABS_W => self.emit_load(mem, OperandSize::S32, R10, RAX, insn.imm), - ebpf::LD_ABS_DW => self.emit_load(mem, OperandSize::S64, R10, RAX, insn.imm), - ebpf::LD_IND_B => { - self.emit_mov(mem, R10, R11); // load mem into R11 - self.emit_alu64(mem, 0x01, src, R11); // add src to R11 - self.emit_load(mem, OperandSize::S8, R11, RAX, insn.imm); // ld R0, mem[src+imm] - } - ebpf::LD_IND_H => { - self.emit_mov(mem, R10, R11); // load mem into R11 - self.emit_alu64(mem, 0x01, src, R11); // add src to R11 - self.emit_load(mem, OperandSize::S16, R11, RAX, insn.imm); // ld R0, mem[src+imm] - } - ebpf::LD_IND_W => { - self.emit_mov(mem, R10, R11); // load mem into R11 - self.emit_alu64(mem, 0x01, src, R11); // add src to R11 - self.emit_load(mem, OperandSize::S32, R11, RAX, insn.imm); // ld R0, mem[src+imm] - } - ebpf::LD_IND_DW => { - self.emit_mov(mem, R10, R11); // load mem into R11 - self.emit_alu64(mem, 0x01, src, R11); // add src to R11 - self.emit_load(mem, OperandSize::S64, R11, RAX, insn.imm); // ld R0, mem[src+imm] - } - - ebpf::LD_DW_IMM => { - insn_ptr += 1; - let second_part = ebpf::get_insn(prog, insn_ptr).imm as u64; - let imm = (insn.imm as u32) as u64 | second_part.wrapping_shl(32); - self.emit_load_imm(mem, dst, imm as i64); - } - - // BPF_LDX class - ebpf::LD_B_REG => self.emit_load(mem, OperandSize::S8, src, dst, insn.off as i32), - ebpf::LD_H_REG => self.emit_load(mem, OperandSize::S16, src, dst, insn.off as i32), - ebpf::LD_W_REG => self.emit_load(mem, OperandSize::S32, src, dst, insn.off as i32), - ebpf::LD_DW_REG => self.emit_load(mem, OperandSize::S64, src, dst, insn.off as i32), - - // BPF_ST class - ebpf::ST_B_IMM => { - self.emit_store_imm32(mem, OperandSize::S8, dst, insn.off as i32, insn.imm) - } - ebpf::ST_H_IMM => { - self.emit_store_imm32(mem, OperandSize::S16, dst, insn.off as i32, insn.imm) - } - ebpf::ST_W_IMM => { - self.emit_store_imm32(mem, OperandSize::S32, dst, insn.off as i32, insn.imm) - } - ebpf::ST_DW_IMM => { - self.emit_store_imm32(mem, OperandSize::S64, dst, insn.off as i32, insn.imm) - } - - // BPF_STX class - ebpf::ST_B_REG => self.emit_store(mem, OperandSize::S8, src, dst, insn.off as i32), - ebpf::ST_H_REG => self.emit_store(mem, OperandSize::S16, src, dst, insn.off as i32), - ebpf::ST_W_REG => self.emit_store(mem, OperandSize::S32, src, dst, insn.off as i32), - ebpf::ST_DW_REG => { - self.emit_store(mem, OperandSize::S64, src, dst, insn.off as i32) - } - ebpf::ST_W_XADD => unimplemented!(), - ebpf::ST_DW_XADD => unimplemented!(), - - // BPF_ALU class - ebpf::ADD32_IMM => self.emit_alu32_imm32(mem, 0x81, 0, dst, insn.imm), - ebpf::ADD32_REG => self.emit_alu32(mem, 0x01, src, dst), - ebpf::SUB32_IMM => self.emit_alu32_imm32(mem, 0x81, 5, dst, insn.imm), - ebpf::SUB32_REG => self.emit_alu32(mem, 0x29, src, dst), - ebpf::MUL32_IMM - | ebpf::MUL32_REG - | ebpf::DIV32_IMM - | ebpf::DIV32_REG - | ebpf::MOD32_IMM - | ebpf::MOD32_REG => { - self.emit_muldivmod(mem, insn_ptr as u16, insn.opc, src, dst, insn.imm) - } - ebpf::OR32_IMM => self.emit_alu32_imm32(mem, 0x81, 1, dst, insn.imm), - ebpf::OR32_REG => self.emit_alu32(mem, 0x09, src, dst), - ebpf::AND32_IMM => self.emit_alu32_imm32(mem, 0x81, 4, dst, insn.imm), - ebpf::AND32_REG => self.emit_alu32(mem, 0x21, src, dst), - ebpf::LSH32_IMM => self.emit_alu32_imm8(mem, 0xc1, 4, dst, insn.imm as i8), - ebpf::LSH32_REG => { - self.emit_mov(mem, src, RCX); - self.emit_alu32(mem, 0xd3, 4, dst); - } - ebpf::RSH32_IMM => self.emit_alu32_imm8(mem, 0xc1, 5, dst, insn.imm as i8), - ebpf::RSH32_REG => { - self.emit_mov(mem, src, RCX); - self.emit_alu32(mem, 0xd3, 5, dst); - } - ebpf::NEG32 => self.emit_alu32(mem, 0xf7, 3, dst), - ebpf::XOR32_IMM => self.emit_alu32_imm32(mem, 0x81, 6, dst, insn.imm), - ebpf::XOR32_REG => self.emit_alu32(mem, 0x31, src, dst), - ebpf::MOV32_IMM => self.emit_alu32_imm32(mem, 0xc7, 0, dst, insn.imm), - ebpf::MOV32_REG => self.emit_mov(mem, src, dst), - ebpf::ARSH32_IMM => self.emit_alu32_imm8(mem, 0xc1, 7, dst, insn.imm as i8), - ebpf::ARSH32_REG => { - self.emit_mov(mem, src, RCX); - self.emit_alu32(mem, 0xd3, 7, dst); - } - ebpf::LE => {} // No-op - ebpf::BE => { - match insn.imm { - 16 => { - // rol - self.emit1(mem, 0x66); // 16-bit override - self.emit_alu32_imm8(mem, 0xc1, 0, dst, 8); - // and - self.emit_alu32_imm32(mem, 0x81, 4, dst, 0xffff); - } - 32 | 64 => { - // bswap - let bit = match insn.imm { - 64 => 1, - _ => 0, - }; - self.emit_basic_rex(mem, bit, 0, dst); - self.emit1(mem, 0x0f); - self.emit1(mem, 0xc8 | (dst & 0b111)); - } - _ => unreachable!(), // Should have been caught by verifier - } - } - - // BPF_ALU64 class - ebpf::ADD64_IMM => self.emit_alu64_imm32(mem, 0x81, 0, dst, insn.imm), - ebpf::ADD64_REG => self.emit_alu64(mem, 0x01, src, dst), - ebpf::SUB64_IMM => self.emit_alu64_imm32(mem, 0x81, 5, dst, insn.imm), - ebpf::SUB64_REG => self.emit_alu64(mem, 0x29, src, dst), - ebpf::MUL64_IMM - | ebpf::MUL64_REG - | ebpf::DIV64_IMM - | ebpf::DIV64_REG - | ebpf::MOD64_IMM - | ebpf::MOD64_REG => { - self.emit_muldivmod(mem, insn_ptr as u16, insn.opc, src, dst, insn.imm) - } - ebpf::OR64_IMM => self.emit_alu64_imm32(mem, 0x81, 1, dst, insn.imm), - ebpf::OR64_REG => self.emit_alu64(mem, 0x09, src, dst), - ebpf::AND64_IMM => self.emit_alu64_imm32(mem, 0x81, 4, dst, insn.imm), - ebpf::AND64_REG => self.emit_alu64(mem, 0x21, src, dst), - ebpf::LSH64_IMM => self.emit_alu64_imm8(mem, 0xc1, 4, dst, insn.imm as i8), - ebpf::LSH64_REG => { - self.emit_mov(mem, src, RCX); - self.emit_alu64(mem, 0xd3, 4, dst); - } - ebpf::RSH64_IMM => self.emit_alu64_imm8(mem, 0xc1, 5, dst, insn.imm as i8), - ebpf::RSH64_REG => { - self.emit_mov(mem, src, RCX); - self.emit_alu64(mem, 0xd3, 5, dst); - } - ebpf::NEG64 => self.emit_alu64(mem, 0xf7, 3, dst), - ebpf::XOR64_IMM => self.emit_alu64_imm32(mem, 0x81, 6, dst, insn.imm), - ebpf::XOR64_REG => self.emit_alu64(mem, 0x31, src, dst), - ebpf::MOV64_IMM => self.emit_load_imm(mem, dst, insn.imm as i64), - ebpf::MOV64_REG => self.emit_mov(mem, src, dst), - ebpf::ARSH64_IMM => self.emit_alu64_imm8(mem, 0xc1, 7, dst, insn.imm as i8), - ebpf::ARSH64_REG => { - self.emit_mov(mem, src, RCX); - self.emit_alu64(mem, 0xd3, 7, dst); - } - - // BPF_JMP class - ebpf::JA => self.emit_jmp(mem, target_pc), - ebpf::JEQ_IMM => { - self.emit_cmp_imm32(mem, dst, insn.imm); - self.emit_jcc(mem, 0x84, target_pc); - } - ebpf::JEQ_REG => { - self.emit_cmp(mem, src, dst); - self.emit_jcc(mem, 0x84, target_pc); - } - ebpf::JGT_IMM => { - self.emit_cmp_imm32(mem, dst, insn.imm); - self.emit_jcc(mem, 0x87, target_pc); - } - ebpf::JGT_REG => { - self.emit_cmp(mem, src, dst); - self.emit_jcc(mem, 0x87, target_pc); - } - ebpf::JGE_IMM => { - self.emit_cmp_imm32(mem, dst, insn.imm); - self.emit_jcc(mem, 0x83, target_pc); - } - ebpf::JGE_REG => { - self.emit_cmp(mem, src, dst); - self.emit_jcc(mem, 0x83, target_pc); - } - ebpf::JLT_IMM => { - self.emit_cmp_imm32(mem, dst, insn.imm); - self.emit_jcc(mem, 0x82, target_pc); - } - ebpf::JLT_REG => { - self.emit_cmp(mem, src, dst); - self.emit_jcc(mem, 0x82, target_pc); - } - ebpf::JLE_IMM => { - self.emit_cmp_imm32(mem, dst, insn.imm); - self.emit_jcc(mem, 0x86, target_pc); - } - ebpf::JLE_REG => { - self.emit_cmp(mem, src, dst); - self.emit_jcc(mem, 0x86, target_pc); - } - ebpf::JSET_IMM => { - self.emit_alu64_imm32(mem, 0xf7, 0, dst, insn.imm); - self.emit_jcc(mem, 0x85, target_pc); - } - ebpf::JSET_REG => { - self.emit_alu64(mem, 0x85, src, dst); - self.emit_jcc(mem, 0x85, target_pc); - } - ebpf::JNE_IMM => { - self.emit_cmp_imm32(mem, dst, insn.imm); - self.emit_jcc(mem, 0x85, target_pc); - } - ebpf::JNE_REG => { - self.emit_cmp(mem, src, dst); - self.emit_jcc(mem, 0x85, target_pc); - } - ebpf::JSGT_IMM => { - self.emit_cmp_imm32(mem, dst, insn.imm); - self.emit_jcc(mem, 0x8f, target_pc); - } - ebpf::JSGT_REG => { - self.emit_cmp(mem, src, dst); - self.emit_jcc(mem, 0x8f, target_pc); - } - ebpf::JSGE_IMM => { - self.emit_cmp_imm32(mem, dst, insn.imm); - self.emit_jcc(mem, 0x8d, target_pc); - } - ebpf::JSGE_REG => { - self.emit_cmp(mem, src, dst); - self.emit_jcc(mem, 0x8d, target_pc); - } - ebpf::JSLT_IMM => { - self.emit_cmp_imm32(mem, dst, insn.imm); - self.emit_jcc(mem, 0x8c, target_pc); - } - ebpf::JSLT_REG => { - self.emit_cmp(mem, src, dst); - self.emit_jcc(mem, 0x8c, target_pc); - } - ebpf::JSLE_IMM => { - self.emit_cmp_imm32(mem, dst, insn.imm); - self.emit_jcc(mem, 0x8e, target_pc); - } - ebpf::JSLE_REG => { - self.emit_cmp(mem, src, dst); - self.emit_jcc(mem, 0x8e, target_pc); - } - - // BPF_JMP32 class - ebpf::JEQ_IMM32 => { - self.emit_cmp32_imm32(mem, dst, insn.imm); - self.emit_jcc(mem, 0x84, target_pc); - } - ebpf::JEQ_REG32 => { - self.emit_cmp32(mem, src, dst); - self.emit_jcc(mem, 0x84, target_pc); - } - ebpf::JGT_IMM32 => { - self.emit_cmp32_imm32(mem, dst, insn.imm); - self.emit_jcc(mem, 0x87, target_pc); - } - ebpf::JGT_REG32 => { - self.emit_cmp32(mem, src, dst); - self.emit_jcc(mem, 0x87, target_pc); - } - ebpf::JGE_IMM32 => { - self.emit_cmp32_imm32(mem, dst, insn.imm); - self.emit_jcc(mem, 0x83, target_pc); - } - ebpf::JGE_REG32 => { - self.emit_cmp32(mem, src, dst); - self.emit_jcc(mem, 0x83, target_pc); - } - ebpf::JLT_IMM32 => { - self.emit_cmp32_imm32(mem, dst, insn.imm); - self.emit_jcc(mem, 0x82, target_pc); - } - ebpf::JLT_REG32 => { - self.emit_cmp32(mem, src, dst); - self.emit_jcc(mem, 0x82, target_pc); - } - ebpf::JLE_IMM32 => { - self.emit_cmp32_imm32(mem, dst, insn.imm); - self.emit_jcc(mem, 0x86, target_pc); - } - ebpf::JLE_REG32 => { - self.emit_cmp32(mem, src, dst); - self.emit_jcc(mem, 0x86, target_pc); - } - ebpf::JSET_IMM32 => { - self.emit_alu32_imm32(mem, 0xf7, 0, dst, insn.imm); - self.emit_jcc(mem, 0x85, target_pc); - } - ebpf::JSET_REG32 => { - self.emit_alu32(mem, 0x85, src, dst); - self.emit_jcc(mem, 0x85, target_pc); - } - ebpf::JNE_IMM32 => { - self.emit_cmp32_imm32(mem, dst, insn.imm); - self.emit_jcc(mem, 0x85, target_pc); - } - ebpf::JNE_REG32 => { - self.emit_cmp32(mem, src, dst); - self.emit_jcc(mem, 0x85, target_pc); - } - ebpf::JSGT_IMM32 => { - self.emit_cmp32_imm32(mem, dst, insn.imm); - self.emit_jcc(mem, 0x8f, target_pc); - } - ebpf::JSGT_REG32 => { - self.emit_cmp32(mem, src, dst); - self.emit_jcc(mem, 0x8f, target_pc); - } - ebpf::JSGE_IMM32 => { - self.emit_cmp32_imm32(mem, dst, insn.imm); - self.emit_jcc(mem, 0x8d, target_pc); - } - ebpf::JSGE_REG32 => { - self.emit_cmp32(mem, src, dst); - self.emit_jcc(mem, 0x8d, target_pc); - } - ebpf::JSLT_IMM32 => { - self.emit_cmp32_imm32(mem, dst, insn.imm); - self.emit_jcc(mem, 0x8c, target_pc); - } - ebpf::JSLT_REG32 => { - self.emit_cmp32(mem, src, dst); - self.emit_jcc(mem, 0x8c, target_pc); - } - ebpf::JSLE_IMM32 => { - self.emit_cmp32_imm32(mem, dst, insn.imm); - self.emit_jcc(mem, 0x8e, target_pc); - } - ebpf::JSLE_REG32 => { - self.emit_cmp32(mem, src, dst); - self.emit_jcc(mem, 0x8e, target_pc); - } - - ebpf::CALL => { - // For JIT, helpers in use MUST be registered at compile time. They can be - // updated later, but not created after compiling (we need the address of the - // helper function in the JIT-compiled program). - if let Some(helper) = helpers.get(&(insn.imm as u32)) { - // We reserve RCX for shifts - self.emit_mov(mem, R9, RCX); - self.emit_call(mem, *helper as usize); - } else { - Err(Error::new( - ErrorKind::Other, - format!( - "[JIT] Error: unknown helper function (id: {:#x})", - insn.imm as u32 - ), - ))?; - }; - } - ebpf::TAIL_CALL => { - unimplemented!() - } - ebpf::EXIT => { - if insn_ptr != prog.len() / ebpf::INSN_SIZE - 1 { - self.emit_jmp(mem, TARGET_PC_EXIT); - }; - } - - _ => { - Err(Error::new( - ErrorKind::Other, - format!( - "[JIT] Error: unknown eBPF opcode {:#2x} (insn #{insn_ptr:?})", - insn.opc - ), - ))?; - } - } - - insn_ptr += 1; - } - - // Epilogue - self.set_anchor(mem, TARGET_PC_EXIT); - - // Move register 0 into rax - if map_register(0) != RAX { - self.emit_mov(mem, map_register(0), RAX); - } - - // Deallocate stack space - self.emit_alu64_imm32(mem, 0x81, 0, RSP, ebpf::STACK_SIZE as i32); - - self.emit_pop(mem, R15); - self.emit_pop(mem, R14); - self.emit_pop(mem, R13); - self.emit_pop(mem, RBX); - self.emit_pop(mem, RBP); - - self.emit1(mem, 0xc3); // ret - - Ok(()) - } - - fn resolve_jumps(&mut self, mem: &mut JitMemory) -> Result<(), Error> { - for jump in &self.jumps { - let target_loc = match self.special_targets.get(&jump.target_pc) { - Some(target) => *target, - None => self.pc_locs[jump.target_pc as usize], - }; - - // Assumes jump offset is at end of instruction - unsafe { - let offset_loc = jump.offset_loc as i32 + std::mem::size_of::() as i32; - let rel = &(target_loc as i32 - offset_loc) as *const i32; - - let offset_ptr = mem.contents.as_ptr().add(jump.offset_loc); - - libc::memcpy( - offset_ptr as *mut libc::c_void, - rel as *const libc::c_void, - std::mem::size_of::(), - ); - } - } - Ok(()) - } -} // impl JitCompiler - -pub struct JitMemory<'a> { - contents: &'a mut [u8], - offset: usize, -} - -impl<'a> JitMemory<'a> { - pub fn new( - prog: &[u8], - helpers: &HashMap, - use_mbuff: bool, - update_data_ptr: bool, - ) -> Result, Error> { - let contents: &mut [u8]; - let mut raw: mem::MaybeUninit<*mut libc::c_void> = mem::MaybeUninit::uninit(); - unsafe { - let size = NUM_PAGES * PAGE_SIZE; - libc::posix_memalign(raw.as_mut_ptr(), PAGE_SIZE, size); - libc::mprotect( - *raw.as_mut_ptr(), - size, - libc::PROT_EXEC | libc::PROT_READ | libc::PROT_WRITE, - ); - std::ptr::write_bytes(*raw.as_mut_ptr(), 0xc3, size); // for now, prepopulate with 'RET' calls - contents = - std::slice::from_raw_parts_mut(*raw.as_mut_ptr() as *mut u8, NUM_PAGES * PAGE_SIZE); - raw.assume_init(); - } - - let mut mem = JitMemory { - contents, - offset: 0, - }; - - let mut jit = JitCompiler::new(); - jit.jit_compile(&mut mem, prog, use_mbuff, update_data_ptr, helpers)?; - jit.resolve_jumps(&mut mem)?; - - Ok(mem) - } - - pub fn get_prog(&self) -> MachineCode { - unsafe { mem::transmute(self.contents.as_ptr()) } - } -} - -impl<'a> Index for JitMemory<'a> { - type Output = u8; - - fn index(&self, _index: usize) -> &u8 { - &self.contents[_index] - } -} - -impl<'a> IndexMut for JitMemory<'a> { - fn index_mut(&mut self, _index: usize) -> &mut u8 { - &mut self.contents[_index] - } -} - -impl<'a> Drop for JitMemory<'a> { - fn drop(&mut self) { - unsafe { - libc::free(self.contents.as_mut_ptr() as *mut libc::c_void); - } - } -} - -impl<'a> std::fmt::Debug for JitMemory<'a> { - fn fmt(&self, fmt: &mut Formatter) -> Result<(), FormatterError> { - fmt.write_str("JIT contents: [")?; - fmt.write_str(" ] | ")?; - fmt.debug_struct("JIT memory") - .field("offset", &self.offset) - .finish() - } -} diff --git a/kernel/crates/rbpf/src/lib.rs b/kernel/crates/rbpf/src/lib.rs deleted file mode 100644 index 0d4fa283..00000000 --- a/kernel/crates/rbpf/src/lib.rs +++ /dev/null @@ -1,1782 +0,0 @@ -// SPDX-License-Identifier: (Apache-2.0 OR MIT) -// Derived from uBPF -// Copyright 2016 6WIND S.A. -// Copyright 2023 Isovalent, Inc. - -//! Virtual machine and JIT compiler for eBPF programs. -#![doc( - html_logo_url = "https://raw.githubusercontent.com/qmonnet/rbpf/main/misc/rbpf.png", - html_favicon_url = "https://raw.githubusercontent.com/qmonnet/rbpf/main/misc/rbpf.ico" -)] -#![warn(missing_docs)] -// There are unused mut warnings due to unsafe code. -#![allow(unused_mut)] -// Allows old-style clippy -#![allow(renamed_and_removed_lints)] -#![cfg_attr( - clippy, - allow( - redundant_field_names, - single_match, - cast_lossless, - doc_markdown, - match_same_arms, - unreadable_literal - ) -)] -// Configures the crate to be `no_std` when `std` feature is disabled. -#![cfg_attr(not(feature = "std"), no_std)] -extern crate alloc; -use alloc::{collections::BTreeMap, format, vec, vec::Vec}; - -use byteorder::{ByteOrder, LittleEndian}; - -type HashMap = BTreeMap; -#[cfg(feature = "cranelift")] -type HashSet = alloc::collections::BTreeSet; -mod asm_parser; -pub mod assembler; -#[cfg(feature = "cranelift")] -mod cranelift; -pub mod disassembler; -pub mod ebpf; -pub mod helpers; -pub mod insn_builder; -mod interpreter; -#[cfg(all(not(windows), feature = "std"))] -mod jit; -#[cfg(not(feature = "std"))] -mod no_std_error; -mod stack; -mod verifier; - -#[cfg(feature = "std")] -pub use std::io::{Error, ErrorKind}; - -/// In no_std we use a custom implementation of the error which acts as a -/// replacement for the io Error. -#[cfg(not(feature = "std"))] -pub use crate::no_std_error::{Error, ErrorKind}; - -/// eBPF verification function that returns an error if the program does not meet its requirements. -/// -/// Some examples of things the verifier may reject the program for: -/// -/// - Program does not terminate. -/// - Unknown instructions. -/// - Bad formed instruction. -/// - Unknown eBPF helper index. -pub type Verifier = fn(prog: &[u8]) -> Result<(), Error>; - -/// eBPF helper function. -pub type Helper = fn(u64, u64, u64, u64, u64) -> u64; - -// A metadata buffer with two offset indications. It can be used in one kind of eBPF VM to simulate -// the use of a metadata buffer each time the program is executed, without the user having to -// actually handle it. The offsets are used to tell the VM where in the buffer the pointers to -// packet data start and end should be stored each time the program is run on a new packet. -struct MetaBuff { - data_offset: usize, - data_end_offset: usize, - buffer: Vec, -} - -/// A virtual machine to run eBPF program. This kind of VM is used for programs expecting to work -/// on a metadata buffer containing pointers to packet data. -/// -/// # Examples -/// -/// ``` -/// let prog = &[ -/// 0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // Load mem from mbuff at offset 8 into R1. -/// 0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ldhx r1[2], r0 -/// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit -/// ]; -/// let mem = &mut [ -/// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd -/// ]; -/// -/// // Just for the example we create our metadata buffer from scratch, and we store the pointers -/// // to packet data start and end in it. -/// let mut mbuff = [0u8; 32]; -/// unsafe { -/// let mut data = mbuff.as_ptr().offset(8) as *mut u64; -/// let mut data_end = mbuff.as_ptr().offset(24) as *mut u64; -/// *data = mem.as_ptr() as u64; -/// *data_end = mem.as_ptr() as u64 + mem.len() as u64; -/// } -/// -/// // Instantiate a VM. -/// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog)).unwrap(); -/// -/// // Provide both a reference to the packet data, and to the metadata buffer. -/// let res = vm.execute_program(mem, &mut mbuff).unwrap(); -/// assert_eq!(res, 0x2211); -/// ``` -pub struct EbpfVmMbuff<'a> { - prog: Option<&'a [u8]>, - verifier: Verifier, - #[cfg(all(not(windows), feature = "std"))] - jit: Option>, - #[cfg(feature = "cranelift")] - cranelift_prog: Option, - helpers: HashMap, -} - -impl<'a> EbpfVmMbuff<'a> { - /// Create a new virtual machine instance, and load an eBPF program into that instance. - /// When attempting to load the program, it passes through a simple verifier. - /// - /// # Examples - /// - /// ``` - /// let prog = &[ - /// 0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // Load mem from mbuff into R1. - /// 0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ldhx r1[2], r0 - /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit - /// ]; - /// - /// // Instantiate a VM. - /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog)).unwrap(); - /// ``` - pub fn new(prog: Option<&'a [u8]>) -> Result, Error> { - if let Some(prog) = prog { - verifier::check(prog)?; - } - - Ok(EbpfVmMbuff { - prog, - verifier: verifier::check, - #[cfg(all(not(windows), feature = "std"))] - jit: None, - #[cfg(feature = "cranelift")] - cranelift_prog: None, - helpers: HashMap::new(), - }) - } - - /// Load a new eBPF program into the virtual machine instance. - /// - /// # Examples - /// - /// ``` - /// let prog1 = &[ - /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0 - /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit - /// ]; - /// let prog2 = &[ - /// 0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // Load mem from mbuff into R1. - /// 0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ldhx r1[2], r0 - /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit - /// ]; - /// - /// // Instantiate a VM. - /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog1)).unwrap(); - /// vm.set_program(prog2).unwrap(); - /// ``` - pub fn set_program(&mut self, prog: &'a [u8]) -> Result<(), Error> { - (self.verifier)(prog)?; - self.prog = Some(prog); - Ok(()) - } - - /// Set a new verifier function. The function should return an `Error` if the program should be - /// rejected by the virtual machine. If a program has been loaded to the VM already, the - /// verifier is immediately run. - /// - /// # Examples - /// - /// ``` - /// use rbpf::{Error, ErrorKind}; - /// use rbpf::ebpf; - /// - /// // Define a simple verifier function. - /// fn verifier(prog: &[u8]) -> Result<(), Error> { - /// let last_insn = ebpf::get_insn(prog, (prog.len() / ebpf::INSN_SIZE) - 1); - /// if last_insn.opc != ebpf::EXIT { - /// return Err(Error::new(ErrorKind::Other, - /// "[Verifier] Error: program does not end with “EXIT” instruction")); - /// } - /// Ok(()) - /// } - /// - /// let prog1 = &[ - /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0 - /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit - /// ]; - /// - /// // Instantiate a VM. - /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog1)).unwrap(); - /// // Change the verifier. - /// vm.set_verifier(verifier).unwrap(); - /// ``` - pub fn set_verifier(&mut self, verifier: Verifier) -> Result<(), Error> { - if let Some(prog) = self.prog { - verifier(prog)?; - } - self.verifier = verifier; - Ok(()) - } - - /// Register a built-in or user-defined helper function in order to use it later from within - /// the eBPF program. The helper is registered into a hashmap, so the `key` can be any `u32`. - /// - /// If using JIT-compiled eBPF programs, be sure to register all helpers before compiling the - /// program. You should be able to change registered helpers after compiling, but not to add - /// new ones (i.e. with new keys). - /// - /// # Examples - /// - /// ``` - /// use rbpf::helpers; - /// - /// // This program was compiled with clang, from a C program containing the following single - /// // instruction: `return bpf_trace_printk("foo %c %c %c\n", 10, 1, 2, 3);` - /// let prog = &[ - /// 0x18, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load 0 as u64 into r1 (That would be - /// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // replaced by tc by the address of - /// // the format string, in the .map - /// // section of the ELF file). - /// 0xb7, 0x02, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, // mov r2, 10 - /// 0xb7, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // mov r3, 1 - /// 0xb7, 0x04, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, // mov r4, 2 - /// 0xb7, 0x05, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, // mov r5, 3 - /// 0x85, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, // call helper with key 6 - /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit - /// ]; - /// - /// // Instantiate a VM. - /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog)).unwrap(); - /// - /// // Register a helper. - /// // On running the program this helper will print the content of registers r3, r4 and r5 to - /// // standard output. - /// # #[cfg(feature = "std")] - /// vm.register_helper(6, helpers::bpf_trace_printf).unwrap(); - /// ``` - pub fn register_helper(&mut self, key: u32, function: Helper) -> Result<(), Error> { - self.helpers.insert(key, function); - Ok(()) - } - - /// Execute the program loaded, with the given packet data and metadata buffer. - /// - /// If the program is made to be compatible with Linux kernel, it is expected to load the - /// address of the beginning and of the end of the memory area used for packet data from the - /// metadata buffer, at some appointed offsets. It is up to the user to ensure that these - /// pointers are correctly stored in the buffer. - /// - /// # Examples - /// - /// ``` - /// let prog = &[ - /// 0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // Load mem from mbuff into R1. - /// 0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ldhx r1[2], r0 - /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit - /// ]; - /// let mem = &mut [ - /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd - /// ]; - /// - /// // Just for the example we create our metadata buffer from scratch, and we store the - /// // pointers to packet data start and end in it. - /// let mut mbuff = [0u8; 32]; - /// unsafe { - /// let mut data = mbuff.as_ptr().offset(8) as *mut u64; - /// let mut data_end = mbuff.as_ptr().offset(24) as *mut u64; - /// *data = mem.as_ptr() as u64; - /// *data_end = mem.as_ptr() as u64 + mem.len() as u64; - /// } - /// - /// // Instantiate a VM. - /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog)).unwrap(); - /// - /// // Provide both a reference to the packet data, and to the metadata buffer. - /// let res = vm.execute_program(mem, &mut mbuff).unwrap(); - /// assert_eq!(res, 0x2211); - /// ``` - pub fn execute_program(&self, mem: &[u8], mbuff: &[u8]) -> Result { - interpreter::execute_program(self.prog, mem, mbuff, &self.helpers) - } - - /// JIT-compile the loaded program. No argument required for this. - /// - /// If using helper functions, be sure to register them into the VM before calling this - /// function. - /// - /// # Examples - /// - /// ``` - /// let prog = &[ - /// 0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // Load mem from mbuff into R1. - /// 0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ldhx r1[2], r0 - /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit - /// ]; - /// - /// // Instantiate a VM. - /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog)).unwrap(); - /// - /// vm.jit_compile(); - /// ``` - #[cfg(all(not(windows), feature = "std"))] - pub fn jit_compile(&mut self) -> Result<(), Error> { - let prog = match self.prog { - Some(prog) => prog, - None => Err(Error::new( - ErrorKind::Other, - "Error: No program set, call prog_set() to load one", - ))?, - }; - self.jit = Some(jit::JitMemory::new(prog, &self.helpers, true, false)?); - Ok(()) - } - - /// Execute the previously JIT-compiled program, with the given packet data and metadata - /// buffer, in a manner very similar to `execute_program()`. - /// - /// If the program is made to be compatible with Linux kernel, it is expected to load the - /// address of the beginning and of the end of the memory area used for packet data from the - /// metadata buffer, at some appointed offsets. It is up to the user to ensure that these - /// pointers are correctly stored in the buffer. - /// - /// # Safety - /// - /// **WARNING:** JIT-compiled assembly code is not safe, in particular there is no runtime - /// check for memory access; so if the eBPF program attempts erroneous accesses, this may end - /// very bad (program may segfault). It may be wise to check that the program works with the - /// interpreter before running the JIT-compiled version of it. - /// - /// For this reason the function should be called from within an `unsafe` bloc. - /// - /// # Examples - /// - /// ``` - /// let prog = &[ - /// 0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // Load mem from mbuff into r1. - /// 0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ldhx r1[2], r0 - /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit - /// ]; - /// let mem = &mut [ - /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd - /// ]; - /// - /// // Just for the example we create our metadata buffer from scratch, and we store the - /// // pointers to packet data start and end in it. - /// let mut mbuff = [0u8; 32]; - /// unsafe { - /// let mut data = mbuff.as_ptr().offset(8) as *mut u64; - /// let mut data_end = mbuff.as_ptr().offset(24) as *mut u64; - /// *data = mem.as_ptr() as u64; - /// *data_end = mem.as_ptr() as u64 + mem.len() as u64; - /// } - /// - /// // Instantiate a VM. - /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog)).unwrap(); - /// - /// # #[cfg(all(not(windows), feature = "std"))] - /// vm.jit_compile(); - /// - /// // Provide both a reference to the packet data, and to the metadata buffer. - /// # #[cfg(all(not(windows), feature = "std"))] - /// unsafe { - /// let res = vm.execute_program_jit(mem, &mut mbuff).unwrap(); - /// assert_eq!(res, 0x2211); - /// } - /// ``` - #[cfg(all(not(windows), feature = "std"))] - pub unsafe fn execute_program_jit( - &self, - mem: &mut [u8], - mbuff: &'a mut [u8], - ) -> Result { - // If packet data is empty, do not send the address of an empty slice; send a null pointer - // as first argument instead, as this is uBPF's behavior (empty packet should not happen - // in the kernel; anyway the verifier would prevent the use of uninitialized registers). - // See `mul_loop` test. - let mem_ptr = match mem.len() { - 0 => std::ptr::null_mut(), - _ => mem.as_ptr() as *mut u8, - }; - // The last two arguments are not used in this function. They would be used if there was a - // need to indicate to the JIT at which offset in the mbuff mem_ptr and mem_ptr + mem.len() - // should be stored; this is what happens with struct EbpfVmFixedMbuff. - match &self.jit { - Some(jit) => Ok(jit.get_prog()( - mbuff.as_ptr() as *mut u8, - mbuff.len(), - mem_ptr, - mem.len(), - 0, - 0, - )), - None => Err(Error::new( - ErrorKind::Other, - "Error: program has not been JIT-compiled", - )), - } - } - - /// Compile the loaded program using the Cranelift JIT. - /// - /// If using helper functions, be sure to register them into the VM before calling this - /// function. - /// - /// # Examples - /// - /// ``` - /// let prog = &[ - /// 0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // Load mem from mbuff into R1. - /// 0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ldhx r1[2], r0 - /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit - /// ]; - /// - /// // Instantiate a VM. - /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog)).unwrap(); - /// - /// vm.cranelift_compile(); - /// ``` - #[cfg(feature = "cranelift")] - pub fn cranelift_compile(&mut self) -> Result<(), Error> { - use crate::cranelift::CraneliftCompiler; - - let prog = match self.prog { - Some(prog) => prog, - None => Err(Error::new( - ErrorKind::Other, - "Error: No program set, call prog_set() to load one", - ))?, - }; - - let mut compiler = CraneliftCompiler::new(self.helpers.clone()); - let program = compiler.compile_function(prog)?; - - self.cranelift_prog = Some(program); - Ok(()) - } - - /// Execute the previously compiled program, with the given packet data and metadata - /// buffer, in a manner very similar to `execute_program()`. - /// - /// If the program is made to be compatible with Linux kernel, it is expected to load the - /// address of the beginning and of the end of the memory area used for packet data from the - /// metadata buffer, at some appointed offsets. It is up to the user to ensure that these - /// pointers are correctly stored in the buffer. - /// - /// - /// # Examples - /// - /// ``` - /// let prog = &[ - /// 0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // Load mem from mbuff into r1. - /// 0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // ldhx r1[2], r0 - /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit - /// ]; - /// let mem = &mut [ - /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd - /// ]; - /// - /// // Just for the example we create our metadata buffer from scratch, and we store the - /// // pointers to packet data start and end in it. - /// let mut mbuff = [0u8; 32]; - /// unsafe { - /// let mut data = mbuff.as_ptr().offset(8) as *mut u64; - /// let mut data_end = mbuff.as_ptr().offset(24) as *mut u64; - /// *data = mem.as_ptr() as u64; - /// *data_end = mem.as_ptr() as u64 + mem.len() as u64; - /// } - /// - /// // Instantiate a VM. - /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog)).unwrap(); - /// - /// vm.cranelift_compile(); - /// - /// // Provide both a reference to the packet data, and to the metadata buffer. - /// let res = vm.execute_program_cranelift(mem, &mut mbuff).unwrap(); - /// assert_eq!(res, 0x2211); - /// ``` - #[cfg(feature = "cranelift")] - pub fn execute_program_cranelift( - &self, - mem: &mut [u8], - mbuff: &'a mut [u8], - ) -> Result { - // If packet data is empty, do not send the address of an empty slice; send a null pointer - // as first argument instead, as this is uBPF's behavior (empty packet should not happen - // in the kernel; anyway the verifier would prevent the use of uninitialized registers). - // See `mul_loop` test. - let mem_ptr = match mem.len() { - 0 => core::ptr::null_mut(), - _ => mem.as_ptr() as *mut u8, - }; - - // The last two arguments are not used in this function. They would be used if there was a - // need to indicate to the JIT at which offset in the mbuff mem_ptr and mem_ptr + mem.len() - // should be stored; this is what happens with struct EbpfVmFixedMbuff. - match &self.cranelift_prog { - Some(prog) => { - Ok(prog.execute(mem_ptr, mem.len(), mbuff.as_ptr() as *mut u8, mbuff.len())) - } - None => Err(Error::new( - ErrorKind::Other, - "Error: program has not been compiled with cranelift", - )), - } - } -} - -/// A virtual machine to run eBPF program. This kind of VM is used for programs expecting to work -/// on a metadata buffer containing pointers to packet data, but it internally handles the buffer -/// so as to save the effort to manually handle the metadata buffer for the user. -/// -/// This struct implements a static internal buffer that is passed to the program. The user has to -/// indicate the offset values at which the eBPF program expects to find the start and the end of -/// packet data in the buffer. On calling the `execute_program()` or `execute_program_jit()` functions, the -/// struct automatically updates the addresses in this static buffer, at the appointed offsets, for -/// the start and the end of the packet data the program is called upon. -/// -/// # Examples -/// -/// This was compiled with clang from the following program, in C: -/// -/// ```c -/// #include -/// #include "path/to/linux/samples/bpf/bpf_helpers.h" -/// -/// SEC(".classifier") -/// int classifier(struct __sk_buff *skb) -/// { -/// void *data = (void *)(long)skb->data; -/// void *data_end = (void *)(long)skb->data_end; -/// -/// // Check program is long enough. -/// if (data + 5 > data_end) -/// return 0; -/// -/// return *((char *)data + 5); -/// } -/// ``` -/// -/// Some small modifications have been brought to have it work, see comments. -/// -/// ``` -/// let prog = &[ -/// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0 -/// // Here opcode 0x61 had to be replace by 0x79 so as to load a 8-bytes long address. -/// // Also, offset 0x4c had to be replace with e.g. 0x40 so as to prevent the two pointers -/// // from overlapping in the buffer. -/// 0x79, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // load pointer to mem from r1[0x40] to r2 -/// 0x07, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // add r2, 5 -/// // Here opcode 0x61 had to be replace by 0x79 so as to load a 8-bytes long address. -/// 0x79, 0x11, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, // load ptr to mem_end from r1[0x50] to r1 -/// 0x2d, 0x12, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, // if r2 > r1 skip 3 instructions -/// 0x71, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load r2 (= *(mem + 5)) into r0 -/// 0x67, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, // r0 >>= 56 -/// 0xc7, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, // r0 <<= 56 (arsh) extend byte sign to u64 -/// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit -/// ]; -/// let mem1 = &mut [ -/// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd -/// ]; -/// let mem2 = &mut [ -/// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0x27 -/// ]; -/// -/// // Instantiate a VM. Note that we provide the start and end offsets for mem pointers. -/// let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap(); -/// -/// // Provide only a reference to the packet data. We do not manage the metadata buffer. -/// let res = vm.execute_program(mem1).unwrap(); -/// assert_eq!(res, 0xffffffffffffffdd); -/// -/// let res = vm.execute_program(mem2).unwrap(); -/// assert_eq!(res, 0x27); -/// ``` -pub struct EbpfVmFixedMbuff<'a> { - parent: EbpfVmMbuff<'a>, - mbuff: MetaBuff, -} - -impl<'a> EbpfVmFixedMbuff<'a> { - /// Create a new virtual machine instance, and load an eBPF program into that instance. - /// When attempting to load the program, it passes through a simple verifier. - /// - /// # Examples - /// - /// ``` - /// let prog = &[ - /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0 - /// 0x79, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem from r1[0x40] to r2 - /// 0x07, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // add r2, 5 - /// 0x79, 0x11, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem_end from r1[0x50] to r1 - /// 0x2d, 0x12, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // if r2 > r1 skip 3 instructions - /// 0x71, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load r2 (= *(mem + 5)) into r0 - /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit - /// ]; - /// - /// // Instantiate a VM. Note that we provide the start and end offsets for mem pointers. - /// let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap(); - /// ``` - pub fn new( - prog: Option<&'a [u8]>, - data_offset: usize, - data_end_offset: usize, - ) -> Result, Error> { - let parent = EbpfVmMbuff::new(prog)?; - let get_buff_len = |x: usize, y: usize| if x >= y { x + 8 } else { y + 8 }; - let buffer = vec![0u8; get_buff_len(data_offset, data_end_offset)]; - let mbuff = MetaBuff { - data_offset, - data_end_offset, - buffer, - }; - Ok(EbpfVmFixedMbuff { parent, mbuff }) - } - - /// Load a new eBPF program into the virtual machine instance. - /// - /// At the same time, load new offsets for storing pointers to start and end of packet data in - /// the internal metadata buffer. - /// - /// # Examples - /// - /// ``` - /// let prog1 = &[ - /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0 - /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit - /// ]; - /// let prog2 = &[ - /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0 - /// 0x79, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem from r1[0x40] to r2 - /// 0x07, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // add r2, 5 - /// 0x79, 0x11, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem_end from r1[0x50] to r1 - /// 0x2d, 0x12, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // if r2 > r1 skip 3 instructions - /// 0x71, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load r2 (= *(mem + 5)) into r0 - /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit - /// ]; - /// - /// let mem = &mut [ - /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0x27, - /// ]; - /// - /// let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog1), 0, 0).unwrap(); - /// vm.set_program(prog2, 0x40, 0x50); - /// - /// let res = vm.execute_program(mem).unwrap(); - /// assert_eq!(res, 0x27); - /// ``` - pub fn set_program( - &mut self, - prog: &'a [u8], - data_offset: usize, - data_end_offset: usize, - ) -> Result<(), Error> { - let get_buff_len = |x: usize, y: usize| if x >= y { x + 8 } else { y + 8 }; - let buffer = vec![0u8; get_buff_len(data_offset, data_end_offset)]; - self.mbuff.buffer = buffer; - self.mbuff.data_offset = data_offset; - self.mbuff.data_end_offset = data_end_offset; - self.parent.set_program(prog)?; - Ok(()) - } - - /// Set a new verifier function. The function should return an `Error` if the program should be - /// rejected by the virtual machine. If a program has been loaded to the VM already, the - /// verifier is immediately run. - /// - /// # Examples - /// - /// ``` - /// use rbpf::{Error, ErrorKind}; - /// use rbpf::ebpf; - /// - /// // Define a simple verifier function. - /// fn verifier(prog: &[u8]) -> Result<(), Error> { - /// let last_insn = ebpf::get_insn(prog, (prog.len() / ebpf::INSN_SIZE) - 1); - /// if last_insn.opc != ebpf::EXIT { - /// return Err(Error::new(ErrorKind::Other, - /// "[Verifier] Error: program does not end with “EXIT” instruction")); - /// } - /// Ok(()) - /// } - /// - /// let prog1 = &[ - /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0 - /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit - /// ]; - /// - /// // Instantiate a VM. - /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog1)).unwrap(); - /// // Change the verifier. - /// vm.set_verifier(verifier).unwrap(); - /// ``` - pub fn set_verifier(&mut self, verifier: Verifier) -> Result<(), Error> { - self.parent.set_verifier(verifier) - } - - /// Register a built-in or user-defined helper function in order to use it later from within - /// the eBPF program. The helper is registered into a hashmap, so the `key` can be any `u32`. - /// - /// If using JIT-compiled eBPF programs, be sure to register all helpers before compiling the - /// program. You should be able to change registered helpers after compiling, but not to add - /// new ones (i.e. with new keys). - /// - /// # Examples - /// - /// ``` - /// #[cfg(feature = "std")] { - /// use rbpf::helpers; - /// - /// // This program was compiled with clang, from a C program containing the following single - /// // instruction: `return bpf_trace_printk("foo %c %c %c\n", 10, 1, 2, 3);` - /// let prog = &[ - /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0 - /// 0x79, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem from r1[0x40] to r2 - /// 0x07, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // add r2, 5 - /// 0x79, 0x11, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem_end from r1[0x50] to r1 - /// 0x2d, 0x12, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, // if r2 > r1 skip 6 instructions - /// 0x71, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load r2 (= *(mem + 5)) into r1 - /// 0xb7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r2, 0 - /// 0xb7, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r3, 0 - /// 0xb7, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r4, 0 - /// 0xb7, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r5, 0 - /// 0x85, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // call helper with key 1 - /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit - /// ]; - /// - /// let mem = &mut [ - /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0x09, - /// ]; - /// - /// // Instantiate a VM. - /// let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap(); - /// - /// // Register a helper. This helper will store the result of the square root of r1 into r0. - /// vm.register_helper(1, helpers::sqrti); - /// - /// let res = vm.execute_program(mem).unwrap(); - /// assert_eq!(res, 3); - /// } - /// ``` - pub fn register_helper( - &mut self, - key: u32, - function: fn(u64, u64, u64, u64, u64) -> u64, - ) -> Result<(), Error> { - self.parent.register_helper(key, function) - } - - /// Execute the program loaded, with the given packet data. - /// - /// If the program is made to be compatible with Linux kernel, it is expected to load the - /// address of the beginning and of the end of the memory area used for packet data from some - /// metadata buffer, which in the case of this VM is handled internally. The offsets at which - /// the addresses should be placed should have be set at the creation of the VM. - /// - /// # Examples - /// - /// ``` - /// let prog = &[ - /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0 - /// 0x79, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem from r1[0x40] to r2 - /// 0x07, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // add r2, 5 - /// 0x79, 0x11, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem_end from r1[0x50] to r1 - /// 0x2d, 0x12, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // if r2 > r1 skip 3 instructions - /// 0x71, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load r2 (= *(mem + 5)) into r0 - /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit - /// ]; - /// let mem = &mut [ - /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd - /// ]; - /// - /// // Instantiate a VM. Note that we provide the start and end offsets for mem pointers. - /// let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap(); - /// - /// // Provide only a reference to the packet data. We do not manage the metadata buffer. - /// let res = vm.execute_program(mem).unwrap(); - /// assert_eq!(res, 0xdd); - /// ``` - pub fn execute_program(&mut self, mem: &'a mut [u8]) -> Result { - let l = self.mbuff.buffer.len(); - // Can this ever happen? Probably not, should be ensured at mbuff creation. - if self.mbuff.data_offset + 8 > l || self.mbuff.data_end_offset + 8 > l { - Err(Error::new(ErrorKind::Other, format!("Error: buffer too small ({:?}), cannot use data_offset {:?} and data_end_offset {:?}", - l, self.mbuff.data_offset, self.mbuff.data_end_offset)))?; - } - LittleEndian::write_u64( - &mut self.mbuff.buffer[(self.mbuff.data_offset)..], - mem.as_ptr() as u64, - ); - LittleEndian::write_u64( - &mut self.mbuff.buffer[(self.mbuff.data_end_offset)..], - mem.as_ptr() as u64 + mem.len() as u64, - ); - self.parent.execute_program(mem, &self.mbuff.buffer) - } - - /// JIT-compile the loaded program. No argument required for this. - /// - /// If using helper functions, be sure to register them into the VM before calling this - /// function. - /// - /// # Examples - /// - /// ``` - /// let prog = &[ - /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0 - /// 0x79, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem from r1[0x40] to r2 - /// 0x07, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // add r2, 5 - /// 0x79, 0x11, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem_end from r1[0x50] to r1 - /// 0x2d, 0x12, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // if r2 > r1 skip 3 instructions - /// 0x71, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load r2 (= *(mem + 5)) into r0 - /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit - /// ]; - /// - /// // Instantiate a VM. Note that we provide the start and end offsets for mem pointers. - /// let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap(); - /// - /// vm.jit_compile(); - /// ``` - #[cfg(all(not(windows), feature = "std"))] - pub fn jit_compile(&mut self) -> Result<(), Error> { - let prog = match self.parent.prog { - Some(prog) => prog, - None => Err(Error::new( - ErrorKind::Other, - "Error: No program set, call prog_set() to load one", - ))?, - }; - self.parent.jit = Some(jit::JitMemory::new(prog, &self.parent.helpers, true, true)?); - Ok(()) - } - - /// Execute the previously JIT-compiled program, with the given packet data, in a manner very - /// similar to `execute_program()`. - /// - /// If the program is made to be compatible with Linux kernel, it is expected to load the - /// address of the beginning and of the end of the memory area used for packet data from some - /// metadata buffer, which in the case of this VM is handled internally. The offsets at which - /// the addresses should be placed should have be set at the creation of the VM. - /// - /// # Safety - /// - /// **WARNING:** JIT-compiled assembly code is not safe, in particular there is no runtime - /// check for memory access; so if the eBPF program attempts erroneous accesses, this may end - /// very bad (program may segfault). It may be wise to check that the program works with the - /// interpreter before running the JIT-compiled version of it. - /// - /// For this reason the function should be called from within an `unsafe` bloc. - /// - /// # Examples - /// - /// ``` - /// let prog = &[ - /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0 - /// 0x79, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem from r1[0x40] to r2 - /// 0x07, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // add r2, 5 - /// 0x79, 0x11, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem_end from r1[0x50] to r1 - /// 0x2d, 0x12, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // if r2 > r1 skip 3 instructions - /// 0x71, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load r2 (= *(mem + 5)) into r0 - /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit - /// ]; - /// let mem = &mut [ - /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd - /// ]; - /// - /// // Instantiate a VM. Note that we provide the start and end offsets for mem pointers. - /// let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap(); - /// - /// # #[cfg(all(not(windows), feature = "std"))] - /// vm.jit_compile(); - /// - /// // Provide only a reference to the packet data. We do not manage the metadata buffer. - /// # #[cfg(all(not(windows), feature = "std"))] - /// unsafe { - /// let res = vm.execute_program_jit(mem).unwrap(); - /// assert_eq!(res, 0xdd); - /// } - /// ``` - // This struct redefines the `execute_program_jit()` function, in order to pass the offsets - // associated with the fixed mbuff. - #[cfg(all(not(windows), feature = "std"))] - pub unsafe fn execute_program_jit(&mut self, mem: &'a mut [u8]) -> Result { - // If packet data is empty, do not send the address of an empty slice; send a null pointer - // as first argument instead, as this is uBPF's behavior (empty packet should not happen - // in the kernel; anyway the verifier would prevent the use of uninitialized registers). - // See `mul_loop` test. - let mem_ptr = match mem.len() { - 0 => core::ptr::null_mut(), - _ => mem.as_ptr() as *mut u8, - }; - - match &self.parent.jit { - Some(jit) => Ok(jit.get_prog()( - self.mbuff.buffer.as_ptr() as *mut u8, - self.mbuff.buffer.len(), - mem_ptr, - mem.len(), - self.mbuff.data_offset, - self.mbuff.data_end_offset, - )), - None => Err(Error::new( - ErrorKind::Other, - "Error: program has not been JIT-compiled", - )), - } - } - - /// Compile the loaded program using the Cranelift JIT. - /// - /// If using helper functions, be sure to register them into the VM before calling this - /// function. - /// - /// # Examples - /// - /// ``` - /// let prog = &[ - /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0 - /// 0x79, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem from r1[0x40] to r2 - /// 0x07, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // add r2, 5 - /// 0x79, 0x11, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem_end from r1[0x50] to r1 - /// 0x2d, 0x12, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // if r2 > r1 skip 3 instructions - /// 0x71, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load r2 (= *(mem + 5)) into r0 - /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit - /// ]; - /// - /// // Instantiate a VM. Note that we provide the start and end offsets for mem pointers. - /// let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap(); - /// - /// vm.cranelift_compile(); - /// ``` - #[cfg(feature = "cranelift")] - pub fn cranelift_compile(&mut self) -> Result<(), Error> { - use crate::cranelift::CraneliftCompiler; - - let prog = match self.parent.prog { - Some(prog) => prog, - None => Err(Error::new( - ErrorKind::Other, - "Error: No program set, call prog_set() to load one", - ))?, - }; - - let mut compiler = CraneliftCompiler::new(self.parent.helpers.clone()); - let program = compiler.compile_function(prog)?; - - self.parent.cranelift_prog = Some(program); - Ok(()) - } - - /// Execute the previously compiled program, with the given packet data and metadata - /// buffer, in a manner very similar to `execute_program()`. - /// - /// If the program is made to be compatible with Linux kernel, it is expected to load the - /// address of the beginning and of the end of the memory area used for packet data from some - /// metadata buffer, which in the case of this VM is handled internally. The offsets at which - /// the addresses should be placed should have be set at the creation of the VM. - /// - /// # Examples - /// - /// ``` - /// let prog = &[ - /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0 - /// 0x79, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem from r1[0x40] to r2 - /// 0x07, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // add r2, 5 - /// 0x79, 0x11, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, // load mem_end from r1[0x50] to r1 - /// 0x2d, 0x12, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // if r2 > r1 skip 3 instructions - /// 0x71, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // load r2 (= *(mem + 5)) into r0 - /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit - /// ]; - /// let mem = &mut [ - /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd - /// ]; - /// - /// // Instantiate a VM. Note that we provide the start and end offsets for mem pointers. - /// let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap(); - /// - /// vm.cranelift_compile(); - /// - /// // Provide only a reference to the packet data. We do not manage the metadata buffer. - /// let res = vm.execute_program_cranelift(mem).unwrap(); - /// assert_eq!(res, 0xdd); - /// ``` - #[cfg(feature = "cranelift")] - pub fn execute_program_cranelift(&mut self, mem: &'a mut [u8]) -> Result { - // If packet data is empty, do not send the address of an empty slice; send a null pointer - // as first argument instead, as this is uBPF's behavior (empty packet should not happen - // in the kernel; anyway the verifier would prevent the use of uninitialized registers). - // See `mul_loop` test. - let mem_ptr = match mem.len() { - 0 => core::ptr::null_mut(), - _ => mem.as_ptr() as *mut u8, - }; - - let l = self.mbuff.buffer.len(); - // Can this ever happen? Probably not, should be ensured at mbuff creation. - if self.mbuff.data_offset + 8 > l || self.mbuff.data_end_offset + 8 > l { - Err(Error::new(ErrorKind::Other, format!("Error: buffer too small ({:?}), cannot use data_offset {:?} and data_end_offset {:?}", - l, self.mbuff.data_offset, self.mbuff.data_end_offset)))?; - } - LittleEndian::write_u64( - &mut self.mbuff.buffer[(self.mbuff.data_offset)..], - mem.as_ptr() as u64, - ); - LittleEndian::write_u64( - &mut self.mbuff.buffer[(self.mbuff.data_end_offset)..], - mem.as_ptr() as u64 + mem.len() as u64, - ); - - match &self.parent.cranelift_prog { - Some(prog) => Ok(prog.execute( - mem_ptr, - mem.len(), - self.mbuff.buffer.as_ptr() as *mut u8, - self.mbuff.buffer.len(), - )), - None => Err(Error::new( - ErrorKind::Other, - "Error: program has not been compiled with cranelift", - )), - } - } -} - -/// A virtual machine to run eBPF program. This kind of VM is used for programs expecting to work -/// directly on the memory area representing packet data. -/// -/// # Examples -/// -/// ``` -/// let prog = &[ -/// 0x71, 0x11, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxb r1[0x04], r1 -/// 0x07, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, // add r1, 0x22 -/// 0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, r1 -/// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit -/// ]; -/// let mem = &mut [ -/// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd -/// ]; -/// -/// // Instantiate a VM. -/// let vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap(); -/// -/// // Provide only a reference to the packet data. -/// let res = vm.execute_program(mem).unwrap(); -/// assert_eq!(res, 0x22cc); -/// ``` -pub struct EbpfVmRaw<'a> { - parent: EbpfVmMbuff<'a>, -} - -impl<'a> EbpfVmRaw<'a> { - /// Create a new virtual machine instance, and load an eBPF program into that instance. - /// When attempting to load the program, it passes through a simple verifier. - /// - /// # Examples - /// - /// ``` - /// let prog = &[ - /// 0x71, 0x11, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxb r1[0x04], r1 - /// 0x07, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, // add r1, 0x22 - /// 0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, r1 - /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit - /// ]; - /// - /// // Instantiate a VM. - /// let vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap(); - /// ``` - pub fn new(prog: Option<&'a [u8]>) -> Result, Error> { - let parent = EbpfVmMbuff::new(prog)?; - Ok(EbpfVmRaw { parent }) - } - - /// Load a new eBPF program into the virtual machine instance. - /// - /// # Examples - /// - /// ``` - /// let prog1 = &[ - /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0 - /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit - /// ]; - /// let prog2 = &[ - /// 0x71, 0x11, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxb r1[0x04], r1 - /// 0x07, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, // add r1, 0x22 - /// 0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, r1 - /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit - /// ]; - /// - /// let mem = &mut [ - /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0x27, - /// ]; - /// - /// let mut vm = rbpf::EbpfVmRaw::new(Some(prog1)).unwrap(); - /// vm.set_program(prog2); - /// - /// let res = vm.execute_program(mem).unwrap(); - /// assert_eq!(res, 0x22cc); - /// ``` - pub fn set_program(&mut self, prog: &'a [u8]) -> Result<(), Error> { - self.parent.set_program(prog)?; - Ok(()) - } - - /// Set a new verifier function. The function should return an `Error` if the program should be - /// rejected by the virtual machine. If a program has been loaded to the VM already, the - /// verifier is immediately run. - /// - /// # Examples - /// - /// ``` - /// use rbpf::{Error, ErrorKind}; - /// use rbpf::ebpf; - /// - /// // Define a simple verifier function. - /// fn verifier(prog: &[u8]) -> Result<(), Error> { - /// let last_insn = ebpf::get_insn(prog, (prog.len() / ebpf::INSN_SIZE) - 1); - /// if last_insn.opc != ebpf::EXIT { - /// return Err(Error::new(ErrorKind::Other, - /// "[Verifier] Error: program does not end with “EXIT” instruction")); - /// } - /// Ok(()) - /// } - /// - /// let prog1 = &[ - /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0 - /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit - /// ]; - /// - /// // Instantiate a VM. - /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog1)).unwrap(); - /// // Change the verifier. - /// vm.set_verifier(verifier).unwrap(); - /// ``` - pub fn set_verifier(&mut self, verifier: Verifier) -> Result<(), Error> { - self.parent.set_verifier(verifier) - } - - /// Register a built-in or user-defined helper function in order to use it later from within - /// the eBPF program. The helper is registered into a hashmap, so the `key` can be any `u32`. - /// - /// If using JIT-compiled eBPF programs, be sure to register all helpers before compiling the - /// program. You should be able to change registered helpers after compiling, but not to add - /// new ones (i.e. with new keys). - /// - /// # Examples - /// - /// ``` - /// #[cfg(feature = "std")] { - /// use rbpf::helpers; - /// - /// let prog = &[ - /// 0x79, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxdw r1, r1[0x00] - /// 0xb7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r2, 0 - /// 0xb7, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r3, 0 - /// 0xb7, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r4, 0 - /// 0xb7, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r5, 0 - /// 0x85, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // call helper with key 1 - /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit - /// ]; - /// - /// let mem = &mut [ - /// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 - /// ]; - /// - /// // Instantiate a VM. - /// let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap(); - /// - /// // Register a helper. This helper will store the result of the square root of r1 into r0. - /// vm.register_helper(1, helpers::sqrti); - /// - /// let res = vm.execute_program(mem).unwrap(); - /// assert_eq!(res, 0x10000000); - /// } - /// ``` - pub fn register_helper( - &mut self, - key: u32, - function: fn(u64, u64, u64, u64, u64) -> u64, - ) -> Result<(), Error> { - self.parent.register_helper(key, function) - } - - /// Register a set of built-in or user-defined helper functions in order to use them later from - /// within the eBPF program. The helpers are registered into a hashmap, so the `key` can be any - /// `u32`. - #[allow(clippy::type_complexity)] - pub fn register_helper_set( - &mut self, - helpers: &HashMap u64>, - ) -> Result<(), Error> { - for (key, function) in helpers { - self.parent.register_helper(*key, *function)?; - } - Ok(()) - } - - /// Execute the program loaded, with the given packet data. - /// - /// # Examples - /// - /// ``` - /// let prog = &[ - /// 0x71, 0x11, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxb r1[0x04], r1 - /// 0x07, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, // add r1, 0x22 - /// 0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, r1 - /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit - /// ]; - /// - /// let mem = &mut [ - /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0x27 - /// ]; - /// - /// let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap(); - /// - /// let res = vm.execute_program(mem).unwrap(); - /// assert_eq!(res, 0x22cc); - /// ``` - pub fn execute_program(&self, mem: &'a mut [u8]) -> Result { - self.parent.execute_program(mem, &[]) - } - - /// JIT-compile the loaded program. No argument required for this. - /// - /// If using helper functions, be sure to register them into the VM before calling this - /// function. - /// - /// # Examples - /// - /// ``` - /// let prog = &[ - /// 0x71, 0x11, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxb r1[0x04], r1 - /// 0x07, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, // add r1, 0x22 - /// 0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, r1 - /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit - /// ]; - /// - /// let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap(); - /// - /// vm.jit_compile(); - /// ``` - #[cfg(all(not(windows), feature = "std"))] - pub fn jit_compile(&mut self) -> Result<(), Error> { - let prog = match self.parent.prog { - Some(prog) => prog, - None => Err(Error::new( - ErrorKind::Other, - "Error: No program set, call prog_set() to load one", - ))?, - }; - self.parent.jit = Some(jit::JitMemory::new( - prog, - &self.parent.helpers, - false, - false, - )?); - Ok(()) - } - - /// Execute the previously JIT-compiled program, with the given packet data, in a manner very - /// similar to `execute_program()`. - /// - /// # Safety - /// - /// **WARNING:** JIT-compiled assembly code is not safe, in particular there is no runtime - /// check for memory access; so if the eBPF program attempts erroneous accesses, this may end - /// very bad (program may segfault). It may be wise to check that the program works with the - /// interpreter before running the JIT-compiled version of it. - /// - /// For this reason the function should be called from within an `unsafe` bloc. - /// - /// # Examples - /// - /// ``` - /// let prog = &[ - /// 0x71, 0x11, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxb r1[0x04], r1 - /// 0x07, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, // add r1, 0x22 - /// 0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, r1 - /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit - /// ]; - /// - /// let mem = &mut [ - /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0x27 - /// ]; - /// - /// let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap(); - /// - /// # #[cfg(all(not(windows), feature = "std"))] - /// vm.jit_compile(); - /// - /// # #[cfg(all(not(windows), feature = "std"))] - /// unsafe { - /// let res = vm.execute_program_jit(mem).unwrap(); - /// assert_eq!(res, 0x22cc); - /// } - /// ``` - #[cfg(all(not(windows), feature = "std"))] - pub unsafe fn execute_program_jit(&self, mem: &'a mut [u8]) -> Result { - let mut mbuff = vec![]; - self.parent.execute_program_jit(mem, &mut mbuff) - } - - /// Compile the loaded program using the Cranelift JIT. - /// - /// If using helper functions, be sure to register them into the VM before calling this - /// function. - /// - /// # Examples - /// - /// ``` - /// let prog = &[ - /// 0x71, 0x11, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxb r1[0x04], r1 - /// 0x07, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, // add r1, 0x22 - /// 0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, r1 - /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit - /// ]; - /// - /// let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap(); - /// - /// vm.cranelift_compile(); - /// ``` - #[cfg(feature = "cranelift")] - pub fn cranelift_compile(&mut self) -> Result<(), Error> { - use crate::cranelift::CraneliftCompiler; - - let prog = match self.parent.prog { - Some(prog) => prog, - None => Err(Error::new( - ErrorKind::Other, - "Error: No program set, call prog_set() to load one", - ))?, - }; - - let mut compiler = CraneliftCompiler::new(self.parent.helpers.clone()); - let program = compiler.compile_function(prog)?; - - self.parent.cranelift_prog = Some(program); - Ok(()) - } - - /// Execute the previously compiled program, with the given packet data, in a manner very - /// similar to `execute_program()`. - /// - /// # Examples - /// - /// ``` - /// let prog = &[ - /// 0x71, 0x11, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // ldxb r1[0x04], r1 - /// 0x07, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, // add r1, 0x22 - /// 0xbf, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, r1 - /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit - /// ]; - /// - /// let mem = &mut [ - /// 0xaa, 0xbb, 0x11, 0x22, 0xcc, 0x27 - /// ]; - /// - /// let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap(); - /// - /// vm.cranelift_compile(); - /// - /// let res = vm.execute_program_cranelift(mem).unwrap(); - /// assert_eq!(res, 0x22cc); - /// ``` - #[cfg(feature = "cranelift")] - pub fn execute_program_cranelift(&self, mem: &'a mut [u8]) -> Result { - let mut mbuff = vec![]; - self.parent.execute_program_cranelift(mem, &mut mbuff) - } -} - -/// A virtual machine to run eBPF program. This kind of VM is used for programs that do not work -/// with any memory area—no metadata buffer, no packet data either. -/// -/// # Examples -/// -/// ``` -/// let prog = &[ -/// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0 -/// 0xb7, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // mov r1, 1 -/// 0xb7, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, // mov r2, 2 -/// 0xb7, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, // mov r3, 3 -/// 0xb7, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, // mov r4, 4 -/// 0xb7, 0x05, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, // mov r5, 5 -/// 0xb7, 0x06, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, // mov r6, 6 -/// 0xb7, 0x07, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, // mov r7, 7 -/// 0xb7, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, // mov r8, 8 -/// 0x4f, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // or r0, r5 -/// 0x47, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, // or r0, 0xa0 -/// 0x57, 0x00, 0x00, 0x00, 0xa3, 0x00, 0x00, 0x00, // and r0, 0xa3 -/// 0xb7, 0x09, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, // mov r9, 0x91 -/// 0x5f, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // and r0, r9 -/// 0x67, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, // lsh r0, 32 -/// 0x67, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, // lsh r0, 22 -/// 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // lsh r0, r8 -/// 0x77, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, // rsh r0, 32 -/// 0x77, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, // rsh r0, 19 -/// 0x7f, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // rsh r0, r7 -/// 0xa7, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, // xor r0, 0x03 -/// 0xaf, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // xor r0, r2 -/// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit -/// ]; -/// -/// // Instantiate a VM. -/// let vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap(); -/// -/// // Provide only a reference to the packet data. -/// let res = vm.execute_program().unwrap(); -/// assert_eq!(res, 0x11); -/// ``` -pub struct EbpfVmNoData<'a> { - parent: EbpfVmRaw<'a>, -} - -impl<'a> EbpfVmNoData<'a> { - /// Create a new virtual machine instance, and load an eBPF program into that instance. - /// When attempting to load the program, it passes through a simple verifier. - /// - /// # Examples - /// - /// ``` - /// let prog = &[ - /// 0xb7, 0x00, 0x00, 0x00, 0x11, 0x22, 0x00, 0x00, // mov r0, 0x2211 - /// 0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, // be16 r0 - /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit - /// ]; - /// - /// // Instantiate a VM. - /// let vm = rbpf::EbpfVmNoData::new(Some(prog)); - /// ``` - pub fn new(prog: Option<&'a [u8]>) -> Result, Error> { - let parent = EbpfVmRaw::new(prog)?; - Ok(EbpfVmNoData { parent }) - } - - /// Load a new eBPF program into the virtual machine instance. - /// - /// # Examples - /// - /// ``` - /// let prog1 = &[ - /// 0xb7, 0x00, 0x00, 0x00, 0x11, 0x22, 0x00, 0x00, // mov r0, 0x2211 - /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit - /// ]; - /// let prog2 = &[ - /// 0xb7, 0x00, 0x00, 0x00, 0x11, 0x22, 0x00, 0x00, // mov r0, 0x2211 - /// 0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, // be16 r0 - /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit - /// ]; - /// - /// let mut vm = rbpf::EbpfVmNoData::new(Some(prog1)).unwrap(); - /// - /// let res = vm.execute_program().unwrap(); - /// assert_eq!(res, 0x2211); - /// - /// vm.set_program(prog2); - /// - /// let res = vm.execute_program().unwrap(); - /// assert_eq!(res, 0x1122); - /// ``` - pub fn set_program(&mut self, prog: &'a [u8]) -> Result<(), Error> { - self.parent.set_program(prog)?; - Ok(()) - } - - /// Set a new verifier function. The function should return an `Error` if the program should be - /// rejected by the virtual machine. If a program has been loaded to the VM already, the - /// verifier is immediately run. - /// - /// # Examples - /// - /// ``` - /// use rbpf::{Error, ErrorKind}; - /// use rbpf::ebpf; - /// - /// // Define a simple verifier function. - /// fn verifier(prog: &[u8]) -> Result<(), Error> { - /// let last_insn = ebpf::get_insn(prog, (prog.len() / ebpf::INSN_SIZE) - 1); - /// if last_insn.opc != ebpf::EXIT { - /// return Err(Error::new(ErrorKind::Other, - /// "[Verifier] Error: program does not end with “EXIT” instruction")); - /// } - /// Ok(()) - /// } - /// - /// let prog1 = &[ - /// 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r0, 0 - /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit - /// ]; - /// - /// // Instantiate a VM. - /// let mut vm = rbpf::EbpfVmMbuff::new(Some(prog1)).unwrap(); - /// // Change the verifier. - /// vm.set_verifier(verifier).unwrap(); - /// ``` - pub fn set_verifier(&mut self, verifier: Verifier) -> Result<(), Error> { - self.parent.set_verifier(verifier) - } - - /// Register a built-in or user-defined helper function in order to use it later from within - /// the eBPF program. The helper is registered into a hashmap, so the `key` can be any `u32`. - /// - /// If using JIT-compiled eBPF programs, be sure to register all helpers before compiling the - /// program. You should be able to change registered helpers after compiling, but not to add - /// new ones (i.e. with new keys). - /// - /// # Examples - /// - /// ``` - /// #[cfg(feature = "std")] { - /// use rbpf::helpers; - /// - /// let prog = &[ - /// 0xb7, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // mov r1, 0x010000000 - /// 0xb7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r2, 0 - /// 0xb7, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r3, 0 - /// 0xb7, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r4, 0 - /// 0xb7, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov r5, 0 - /// 0x85, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // call helper with key 1 - /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit - /// ]; - /// - /// let mut vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap(); - /// - /// // Register a helper. This helper will store the result of the square root of r1 into r0. - /// vm.register_helper(1, helpers::sqrti).unwrap(); - /// - /// let res = vm.execute_program().unwrap(); - /// assert_eq!(res, 0x1000); - /// } - /// ``` - pub fn register_helper( - &mut self, - key: u32, - function: fn(u64, u64, u64, u64, u64) -> u64, - ) -> Result<(), Error> { - self.parent.register_helper(key, function) - } - - /// JIT-compile the loaded program. No argument required for this. - /// - /// If using helper functions, be sure to register them into the VM before calling this - /// function. - /// - /// # Examples - /// - /// ``` - /// let prog = &[ - /// 0xb7, 0x00, 0x00, 0x00, 0x11, 0x22, 0x00, 0x00, // mov r0, 0x2211 - /// 0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, // be16 r0 - /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit - /// ]; - /// - /// let mut vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap(); - /// - /// - /// vm.jit_compile(); - /// ``` - #[cfg(all(not(windows), feature = "std"))] - pub fn jit_compile(&mut self) -> Result<(), Error> { - self.parent.jit_compile() - } - - /// Execute the program loaded, without providing pointers to any memory area whatsoever. - /// - /// # Examples - /// - /// ``` - /// let prog = &[ - /// 0xb7, 0x00, 0x00, 0x00, 0x11, 0x22, 0x00, 0x00, // mov r0, 0x2211 - /// 0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, // be16 r0 - /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit - /// ]; - /// - /// let vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap(); - /// - /// // For this kind of VM, the `execute_program()` function needs no argument. - /// let res = vm.execute_program().unwrap(); - /// assert_eq!(res, 0x1122); - /// ``` - pub fn execute_program(&self) -> Result { - self.parent.execute_program(&mut []) - } - - /// Execute the previously JIT-compiled program, without providing pointers to any memory area - /// whatsoever, in a manner very similar to `execute_program()`. - /// - /// # Safety - /// - /// **WARNING:** JIT-compiled assembly code is not safe, in particular there is no runtime - /// check for memory access; so if the eBPF program attempts erroneous accesses, this may end - /// very bad (program may segfault). It may be wise to check that the program works with the - /// interpreter before running the JIT-compiled version of it. - /// - /// For this reason the function should be called from within an `unsafe` bloc. - /// - /// # Examples - /// - /// ``` - /// let prog = &[ - /// 0xb7, 0x00, 0x00, 0x00, 0x11, 0x22, 0x00, 0x00, // mov r0, 0x2211 - /// 0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, // be16 r0 - /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit - /// ]; - /// - /// let mut vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap(); - /// - /// # #[cfg(all(not(windows), feature = "std"))] - /// vm.jit_compile(); - /// - /// # #[cfg(all(not(windows), feature = "std"))] - /// unsafe { - /// let res = vm.execute_program_jit().unwrap(); - /// assert_eq!(res, 0x1122); - /// } - /// ``` - #[cfg(all(not(windows), feature = "std"))] - pub unsafe fn execute_program_jit(&self) -> Result { - self.parent.execute_program_jit(&mut []) - } - - /// Compile the loaded program using the Cranelift JIT. - /// - /// If using helper functions, be sure to register them into the VM before calling this - /// function. - /// - /// # Examples - /// - /// ``` - /// let prog = &[ - /// 0xb7, 0x00, 0x00, 0x00, 0x11, 0x22, 0x00, 0x00, // mov r0, 0x2211 - /// 0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, // be16 r0 - /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit - /// ]; - /// - /// let mut vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap(); - /// - /// - /// vm.cranelift_compile(); - /// ``` - #[cfg(feature = "cranelift")] - pub fn cranelift_compile(&mut self) -> Result<(), Error> { - self.parent.cranelift_compile() - } - - /// Execute the previously JIT-compiled program, without providing pointers to any memory area - /// whatsoever, in a manner very similar to `execute_program()`. - /// - /// # Examples - /// - /// ``` - /// let prog = &[ - /// 0xb7, 0x00, 0x00, 0x00, 0x11, 0x22, 0x00, 0x00, // mov r0, 0x2211 - /// 0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, // be16 r0 - /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // exit - /// ]; - /// - /// let mut vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap(); - /// - /// vm.cranelift_compile(); - /// - /// let res = vm.execute_program_cranelift().unwrap(); - /// assert_eq!(res, 0x1122); - /// ``` - #[cfg(feature = "cranelift")] - pub fn execute_program_cranelift(&self) -> Result { - self.parent.execute_program_cranelift(&mut []) - } -} - -/// EbpfVm with Owned data -pub struct EbpfVmRawOwned { - parent: EbpfVmRaw<'static>, - data_len: usize, - data_cap: usize, -} - -impl EbpfVmRawOwned { - /// Create a new virtual machine instance, and load an eBPF program into that instance. - /// When attempting to load the program, it passes through a simple verifier. - pub fn new(prog: Option>) -> Result { - let (prog, data_len, data_cap) = match prog { - Some(prog) => { - let data_len = prog.len(); - let data_cap = prog.capacity(); - let slice = prog.leak(); - let slice = unsafe { core::slice::from_raw_parts(slice.as_ptr(), data_len) }; - (Some(slice), data_len, data_cap) - } - None => (None, 0, 0), - }; - let parent = EbpfVmRaw::new(prog)?; - Ok(Self { - parent, - data_len, - data_cap, - }) - } - /// Load a new eBPF program into the virtual machine instance - pub fn set_program(&mut self, prog: Vec) -> Result<(), Error> { - self.data_len = prog.len(); - self.data_cap = prog.capacity(); - let slice = prog.leak(); - self.parent.set_program(slice)?; - Ok(()) - } - - /// Set a new verifier function. The function should return an Error if the program should be rejected by the virtual machine. - /// If a program has been loaded to the VM already, the verifier is immediately run. - pub fn set_verifier(&mut self, verifier: Verifier) -> Result<(), Error> { - self.parent.set_verifier(verifier) - } - - /// Register a built-in or user-defined helper function in order to use it later from within the eBPF program. - /// The helper is registered into a hashmap, so the key can be any u32. - /// If using JIT-compiled eBPF programs, be sure to register all helpers before compiling the program. - /// You should be able to change registered helpers after compiling, but not to add new ones (i. e. with new keys). - pub fn register_helper( - &mut self, - key: u32, - function: fn(u64, u64, u64, u64, u64) -> u64, - ) -> Result<(), Error> { - self.parent.register_helper(key, function) - } - - /// Register a set of built-in or user-defined helper functions in order to use them later from - /// within the eBPF program. The helpers are registered into a hashmap, so the `key` can be any - /// `u32`. - #[allow(clippy::type_complexity)] - pub fn register_helper_set( - &mut self, - helpers: &HashMap u64>, - ) -> Result<(), Error> { - for (key, function) in helpers { - self.parent.register_helper(*key, *function)?; - } - Ok(()) - } - - /// Execute the previously JIT-compiled program, with the given packet data, in a manner very similar to execute_program(). - /// - /// Safety - /// - /// **WARNING:** JIT-compiled assembly code is not safe, in particular there is no runtime check for memory access; - /// so if the eBPF program attempts erroneous accesses, this may end very bad (program may segfault). - /// It may be wise to check that the program works with the interpreter before running the JIT-compiled version of it. - /// - /// For this reason the function should be called from within an unsafe bloc. - pub fn execute_program(&self, mem: &mut [u8]) -> Result { - self.parent.execute_program(mem) - } -} - -impl Drop for EbpfVmRawOwned { - fn drop(&mut self) { - match self.parent.parent.prog { - Some(prog) => unsafe { - let ptr = prog.as_ptr(); - let _prog = Vec::from_raw_parts(ptr as *mut u8, self.data_len, self.data_cap); - }, - None => {} - }; - } -} diff --git a/kernel/crates/rbpf/src/no_std_error.rs b/kernel/crates/rbpf/src/no_std_error.rs deleted file mode 100644 index f502eecf..00000000 --- a/kernel/crates/rbpf/src/no_std_error.rs +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-License-Identifier: (Apache-2.0 OR MIT) - -//! This module provides a simple implementation of the Error struct that is -//! used as a drop-in replacement for `std::io::Error` when using `rbpf` in `no_std`. - -use alloc::string::String; - -/// Implementation of Error for no_std applications. -/// Ensures that the existing code can use it with the same interface -/// as the Error from std::io::Error. -#[derive(Debug)] -pub struct Error { - #[allow(dead_code)] - kind: ErrorKind, - #[allow(dead_code)] - error: String, -} - -impl Error { - /// New function exposing the same signature as `std::io::Error::new`. - #[allow(dead_code)] - pub fn new>(kind: ErrorKind, error: S) -> Error { - Error { - kind, - error: error.into(), - } - } -} - -/// The current version of `rbpf` only uses the [`Other`](ErrorKind::Other) variant -/// from the [std::io::ErrorKind] enum. If a dependency on other variants were -/// introduced in the future, this enum needs to be updated accordingly to maintain -/// compatibility with the real `ErrorKind`. The reason all available variants -/// aren't included in the first place is that [std::io::ErrorKind] exposes -/// 40 variants, and not all of them are meaningful under `no_std`. -#[derive(Debug)] -pub enum ErrorKind { - /// The no_std code only uses this variant. - #[allow(dead_code)] - Other, -} diff --git a/kernel/crates/rbpf/src/stack.rs b/kernel/crates/rbpf/src/stack.rs deleted file mode 100644 index c1596856..00000000 --- a/kernel/crates/rbpf/src/stack.rs +++ /dev/null @@ -1,75 +0,0 @@ -use crate::{ebpf::STACK_SIZE, vec, Vec}; - -pub struct StackFrame { - return_address: u64, - saved_registers: [u64; 4], - sp: u64, - frame: Vec, -} - -impl StackFrame { - /// Create a new stack frame - /// - /// The stack frame is created with a capacity of `STACK_SIZE` == 512 bytes - pub fn new() -> Self { - Self { - sp: 0, - return_address: 0, - saved_registers: [0; 4], - frame: vec![0; STACK_SIZE], - } - } - - /// Create a new stack frame with a given capacity - #[allow(unused)] - pub fn with_capacity(capacity: usize) -> Self { - Self { - sp: 0, - return_address: 0, - saved_registers: [0; 4], - frame: vec![0; capacity], - } - } - - /// The capacity of the stack frame - pub fn len(&self) -> usize { - self.frame.len() - } - - pub fn as_ptr(&self) -> *const u8 { - self.frame.as_ptr() - } - - pub fn as_slice(&self) -> &[u8] { - self.frame.as_slice() - } - /// Save the callee-saved registers - pub fn save_registers(&mut self, regs: &[u64]) { - self.saved_registers.copy_from_slice(regs); - } - - /// Get the callee-saved registers - pub fn get_registers(&self) -> [u64; 4] { - self.saved_registers - } - - /// Save the return address - pub fn save_return_address(&mut self, address: u64) { - self.return_address = address; - } - - /// Get the return address - pub fn get_return_address(&self) -> u64 { - self.return_address - } - - /// Save the stack pointer - pub fn save_sp(&mut self, sp: u64) { - self.sp = sp; - } - - /// Get the stack pointer - pub fn get_sp(&self) -> u64 { - self.sp - } -} diff --git a/kernel/crates/rbpf/src/verifier.rs b/kernel/crates/rbpf/src/verifier.rs deleted file mode 100644 index 4c9cf250..00000000 --- a/kernel/crates/rbpf/src/verifier.rs +++ /dev/null @@ -1,386 +0,0 @@ -// SPDX-License-Identifier: (Apache-2.0 OR MIT) -// Derived from uBPF -// Copyright 2015 Big Switch Networks, Inc -// (uBPF: safety checks, originally in C) -// Copyright 2016 6WIND S.A. -// (Translation to Rust) - -// This “verifier” performs simple checks when the eBPF program is loaded into the VM (before it is -// interpreted or JIT-compiled). It has nothing to do with the much more elaborated verifier inside -// Linux kernel. There is no verification regarding the program flow control (should be a Direct -// Acyclic Graph) or the consistency for registers usage (the verifier of the kernel assigns types -// to the registers and is much stricter). -// -// On the other hand, rbpf is not expected to run in kernel space. -// -// Improving the verifier would be nice, but this is not trivial (and Linux kernel is under GPL -// license, so we cannot copy it). -// -// Contrary to the verifier of the Linux kernel, this one does not modify the bytecode at all. - -use alloc::format; - -use crate::{ebpf, Error, ErrorKind}; - -fn reject>(msg: S) -> Result<(), Error> { - let full_msg = format!("[Verifier] Error: {}", msg.as_ref()); - Err(Error::new(ErrorKind::Other, full_msg)) -} - -fn check_prog_len(prog: &[u8]) -> Result<(), Error> { - if prog.len() % ebpf::INSN_SIZE != 0 { - reject(format!( - "eBPF program length must be a multiple of {:?} octets", - ebpf::INSN_SIZE - ))?; - } - if prog.len() > ebpf::PROG_MAX_SIZE { - reject(format!( - "eBPF program length limited to {:?}, here {:?}", - ebpf::PROG_MAX_INSNS, - prog.len() / ebpf::INSN_SIZE - ))?; - } - - if prog.is_empty() { - reject("no program set, call set_program() to load one")?; - } - let last_opc = ebpf::get_insn(prog, (prog.len() / ebpf::INSN_SIZE) - 1).opc; - if last_opc & ebpf::BPF_CLS_MASK != ebpf::BPF_JMP { - reject("program does not end with “EXIT” instruction")?; - } - - Ok(()) -} - -fn check_imm_endian(insn: &ebpf::Insn, insn_ptr: usize) -> Result<(), Error> { - match insn.imm { - 16 | 32 | 64 => Ok(()), - _ => reject(format!( - "unsupported argument for LE/BE (insn #{insn_ptr:?})" - )), - } -} - -fn check_load_dw(prog: &[u8], insn_ptr: usize) -> Result<(), Error> { - // We know we can reach next insn since we enforce an EXIT insn at the end of program, while - // this function should be called only for LD_DW insn, that cannot be last in program. - let next_insn = ebpf::get_insn(prog, insn_ptr + 1); - if next_insn.opc != 0 { - reject(format!("incomplete LD_DW instruction (insn #{insn_ptr:?})"))?; - } - - Ok(()) -} - -fn check_jmp_offset(prog: &[u8], insn_ptr: usize) -> Result<(), Error> { - let insn = ebpf::get_insn(prog, insn_ptr); - if insn.off == -1 { - reject(format!("infinite loop (insn #{insn_ptr:?})"))?; - } - - let dst_insn_ptr = insn_ptr as isize + 1 + insn.off as isize; - if dst_insn_ptr < 0 || dst_insn_ptr as usize >= (prog.len() / ebpf::INSN_SIZE) { - reject(format!( - "jump out of code to #{dst_insn_ptr:?} (insn #{insn_ptr:?})" - ))?; - } - - let dst_insn = ebpf::get_insn(prog, dst_insn_ptr as usize); - if dst_insn.opc == 0 { - reject(format!( - "jump to middle of LD_DW at #{dst_insn_ptr:?} (insn #{insn_ptr:?})" - ))?; - } - - Ok(()) -} - -fn check_registers(insn: &ebpf::Insn, store: bool, insn_ptr: usize) -> Result<(), Error> { - if insn.src > 10 { - reject(format!("invalid source register (insn #{insn_ptr:?})"))?; - } - - match (insn.dst, store) { - (0..=9, _) | (10, true) => Ok(()), - (10, false) => reject(format!( - "cannot write into register r10 (insn #{insn_ptr:?})" - )), - (_, _) => reject(format!("invalid destination register (insn #{insn_ptr:?})")), - } -} - -pub fn check(prog: &[u8]) -> Result<(), Error> { - check_prog_len(prog)?; - - let mut insn_ptr: usize = 0; - while insn_ptr * ebpf::INSN_SIZE < prog.len() { - let insn = ebpf::get_insn(prog, insn_ptr); - let mut store = false; - - match insn.opc { - // BPF_LD class - ebpf::LD_ABS_B => {} - ebpf::LD_ABS_H => {} - ebpf::LD_ABS_W => {} - ebpf::LD_ABS_DW => {} - ebpf::LD_IND_B => {} - ebpf::LD_IND_H => {} - ebpf::LD_IND_W => {} - ebpf::LD_IND_DW => {} - - ebpf::LD_DW_IMM => { - store = true; - check_load_dw(prog, insn_ptr)?; - insn_ptr += 1; - } - - // BPF_LDX class - ebpf::LD_B_REG => {} - ebpf::LD_H_REG => {} - ebpf::LD_W_REG => {} - ebpf::LD_DW_REG => {} - - // BPF_ST class - ebpf::ST_B_IMM => store = true, - ebpf::ST_H_IMM => store = true, - ebpf::ST_W_IMM => store = true, - ebpf::ST_DW_IMM => store = true, - - // BPF_STX class - ebpf::ST_B_REG => store = true, - ebpf::ST_H_REG => store = true, - ebpf::ST_W_REG => store = true, - ebpf::ST_DW_REG => store = true, - ebpf::ST_W_XADD => { - unimplemented!(); - } - ebpf::ST_DW_XADD => { - unimplemented!(); - } - - // BPF_ALU class - ebpf::ADD32_IMM => {} - ebpf::ADD32_REG => {} - ebpf::SUB32_IMM => {} - ebpf::SUB32_REG => {} - ebpf::MUL32_IMM => {} - ebpf::MUL32_REG => {} - ebpf::DIV32_IMM => {} - ebpf::DIV32_REG => {} - ebpf::OR32_IMM => {} - ebpf::OR32_REG => {} - ebpf::AND32_IMM => {} - ebpf::AND32_REG => {} - ebpf::LSH32_IMM => {} - ebpf::LSH32_REG => {} - ebpf::RSH32_IMM => {} - ebpf::RSH32_REG => {} - ebpf::NEG32 => {} - ebpf::MOD32_IMM => {} - ebpf::MOD32_REG => {} - ebpf::XOR32_IMM => {} - ebpf::XOR32_REG => {} - ebpf::MOV32_IMM => {} - ebpf::MOV32_REG => {} - ebpf::ARSH32_IMM => {} - ebpf::ARSH32_REG => {} - ebpf::LE => { - check_imm_endian(&insn, insn_ptr)?; - } - ebpf::BE => { - check_imm_endian(&insn, insn_ptr)?; - } - - // BPF_ALU64 class - ebpf::ADD64_IMM => {} - ebpf::ADD64_REG => {} - ebpf::SUB64_IMM => {} - ebpf::SUB64_REG => {} - ebpf::MUL64_IMM => {} - ebpf::MUL64_REG => {} - ebpf::DIV64_IMM => {} - ebpf::DIV64_REG => {} - ebpf::OR64_IMM => {} - ebpf::OR64_REG => {} - ebpf::AND64_IMM => {} - ebpf::AND64_REG => {} - ebpf::LSH64_IMM => {} - ebpf::LSH64_REG => {} - ebpf::RSH64_IMM => {} - ebpf::RSH64_REG => {} - ebpf::NEG64 => {} - ebpf::MOD64_IMM => {} - ebpf::MOD64_REG => {} - ebpf::XOR64_IMM => {} - ebpf::XOR64_REG => {} - ebpf::MOV64_IMM => {} - ebpf::MOV64_REG => {} - ebpf::ARSH64_IMM => {} - ebpf::ARSH64_REG => {} - - // BPF_JMP class - ebpf::JA => { - check_jmp_offset(prog, insn_ptr)?; - } - ebpf::JEQ_IMM => { - check_jmp_offset(prog, insn_ptr)?; - } - ebpf::JEQ_REG => { - check_jmp_offset(prog, insn_ptr)?; - } - ebpf::JGT_IMM => { - check_jmp_offset(prog, insn_ptr)?; - } - ebpf::JGT_REG => { - check_jmp_offset(prog, insn_ptr)?; - } - ebpf::JGE_IMM => { - check_jmp_offset(prog, insn_ptr)?; - } - ebpf::JGE_REG => { - check_jmp_offset(prog, insn_ptr)?; - } - ebpf::JLT_IMM => { - check_jmp_offset(prog, insn_ptr)?; - } - ebpf::JLT_REG => { - check_jmp_offset(prog, insn_ptr)?; - } - ebpf::JLE_IMM => { - check_jmp_offset(prog, insn_ptr)?; - } - ebpf::JLE_REG => { - check_jmp_offset(prog, insn_ptr)?; - } - ebpf::JSET_IMM => { - check_jmp_offset(prog, insn_ptr)?; - } - ebpf::JSET_REG => { - check_jmp_offset(prog, insn_ptr)?; - } - ebpf::JNE_IMM => { - check_jmp_offset(prog, insn_ptr)?; - } - ebpf::JNE_REG => { - check_jmp_offset(prog, insn_ptr)?; - } - ebpf::JSGT_IMM => { - check_jmp_offset(prog, insn_ptr)?; - } - ebpf::JSGT_REG => { - check_jmp_offset(prog, insn_ptr)?; - } - ebpf::JSGE_IMM => { - check_jmp_offset(prog, insn_ptr)?; - } - ebpf::JSGE_REG => { - check_jmp_offset(prog, insn_ptr)?; - } - ebpf::JSLT_IMM => { - check_jmp_offset(prog, insn_ptr)?; - } - ebpf::JSLT_REG => { - check_jmp_offset(prog, insn_ptr)?; - } - ebpf::JSLE_IMM => { - check_jmp_offset(prog, insn_ptr)?; - } - ebpf::JSLE_REG => { - check_jmp_offset(prog, insn_ptr)?; - } - - // BPF_JMP32 class - ebpf::JEQ_IMM32 => { - check_jmp_offset(prog, insn_ptr)?; - } - ebpf::JEQ_REG32 => { - check_jmp_offset(prog, insn_ptr)?; - } - ebpf::JGT_IMM32 => { - check_jmp_offset(prog, insn_ptr)?; - } - ebpf::JGT_REG32 => { - check_jmp_offset(prog, insn_ptr)?; - } - ebpf::JGE_IMM32 => { - check_jmp_offset(prog, insn_ptr)?; - } - ebpf::JGE_REG32 => { - check_jmp_offset(prog, insn_ptr)?; - } - ebpf::JLT_IMM32 => { - check_jmp_offset(prog, insn_ptr)?; - } - ebpf::JLT_REG32 => { - check_jmp_offset(prog, insn_ptr)?; - } - ebpf::JLE_IMM32 => { - check_jmp_offset(prog, insn_ptr)?; - } - ebpf::JLE_REG32 => { - check_jmp_offset(prog, insn_ptr)?; - } - ebpf::JSET_IMM32 => { - check_jmp_offset(prog, insn_ptr)?; - } - ebpf::JSET_REG32 => { - check_jmp_offset(prog, insn_ptr)?; - } - ebpf::JNE_IMM32 => { - check_jmp_offset(prog, insn_ptr)?; - } - ebpf::JNE_REG32 => { - check_jmp_offset(prog, insn_ptr)?; - } - ebpf::JSGT_IMM32 => { - check_jmp_offset(prog, insn_ptr)?; - } - ebpf::JSGT_REG32 => { - check_jmp_offset(prog, insn_ptr)?; - } - ebpf::JSGE_IMM32 => { - check_jmp_offset(prog, insn_ptr)?; - } - ebpf::JSGE_REG32 => { - check_jmp_offset(prog, insn_ptr)?; - } - ebpf::JSLT_IMM32 => { - check_jmp_offset(prog, insn_ptr)?; - } - ebpf::JSLT_REG32 => { - check_jmp_offset(prog, insn_ptr)?; - } - ebpf::JSLE_IMM32 => { - check_jmp_offset(prog, insn_ptr)?; - } - ebpf::JSLE_REG32 => { - check_jmp_offset(prog, insn_ptr)?; - } - - ebpf::CALL => {} - ebpf::TAIL_CALL => { - unimplemented!() - } - ebpf::EXIT => {} - - _ => { - reject(format!( - "unknown eBPF opcode {:#2x} (insn #{insn_ptr:?})", - insn.opc - ))?; - } - } - - check_registers(&insn, store, insn_ptr)?; - - insn_ptr += 1; - } - - // insn_ptr should now be equal to number of instructions. - if insn_ptr != prog.len() / ebpf::INSN_SIZE { - reject(format!("jumped out of code to #{insn_ptr:?}"))?; - } - - Ok(()) -} diff --git a/kernel/crates/rbpf/tests/assembler.rs b/kernel/crates/rbpf/tests/assembler.rs deleted file mode 100644 index 0640772c..00000000 --- a/kernel/crates/rbpf/tests/assembler.rs +++ /dev/null @@ -1,655 +0,0 @@ -// SPDX-License-Identifier: (Apache-2.0 OR MIT) -// Copyright 2017 Rich Lane - -#![allow(clippy::unreadable_literal)] - -extern crate rbpf; -mod common; - -use common::{TCP_SACK_ASM, TCP_SACK_BIN}; -use rbpf::{assembler::assemble, ebpf}; - -fn asm(src: &str) -> Result, String> { - Ok(ebpf::to_insn_vec(&(assemble(src))?)) -} - -fn insn(opc: u8, dst: u8, src: u8, off: i16, imm: i32) -> ebpf::Insn { - ebpf::Insn { - opc, - dst, - src, - off, - imm, - } -} - -#[test] -fn test_empty() { - assert_eq!(asm(""), Ok(vec![])); -} - -// Example for InstructionType::NoOperand. -#[test] -fn test_exit() { - assert_eq!(asm("exit"), Ok(vec![insn(ebpf::EXIT, 0, 0, 0, 0)])); -} - -// Example for InstructionType::AluBinary. -#[test] -fn test_add64() { - assert_eq!( - asm("add64 r1, r3"), - Ok(vec![insn(ebpf::ADD64_REG, 1, 3, 0, 0)]) - ); - assert_eq!( - asm("add64 r1, 5"), - Ok(vec![insn(ebpf::ADD64_IMM, 1, 0, 0, 5)]) - ); -} - -// Example for InstructionType::AluUnary. -#[test] -fn test_neg64() { - assert_eq!(asm("neg64 r1"), Ok(vec![insn(ebpf::NEG64, 1, 0, 0, 0)])); -} - -// Example for InstructionType::LoadReg. -#[test] -fn test_ldxw() { - assert_eq!( - asm("ldxw r1, [r2+5]"), - Ok(vec![insn(ebpf::LD_W_REG, 1, 2, 5, 0)]) - ); -} - -// Example for InstructionType::StoreImm. -#[test] -fn test_stw() { - assert_eq!( - asm("stw [r2+5], 7"), - Ok(vec![insn(ebpf::ST_W_IMM, 2, 0, 5, 7)]) - ); -} - -// Example for InstructionType::StoreReg. -#[test] -fn test_stxw() { - assert_eq!( - asm("stxw [r2+5], r8"), - Ok(vec![insn(ebpf::ST_W_REG, 2, 8, 5, 0)]) - ); -} - -// Example for InstructionType::JumpUnconditional. -#[test] -fn test_ja() { - assert_eq!(asm("ja +8"), Ok(vec![insn(ebpf::JA, 0, 0, 8, 0)])); - assert_eq!(asm("ja -3"), Ok(vec![insn(ebpf::JA, 0, 0, -3, 0)])); -} - -// Example for InstructionType::JumpConditional. -#[test] -fn test_jeq() { - assert_eq!( - asm("jeq r1, 4, +8"), - Ok(vec![insn(ebpf::JEQ_IMM, 1, 0, 8, 4)]) - ); - assert_eq!( - asm("jeq r1, r3, +8"), - Ok(vec![insn(ebpf::JEQ_REG, 1, 3, 8, 0)]) - ); -} - -// Example for InstructionType::Call. -#[test] -fn test_call() { - assert_eq!(asm("call 300"), Ok(vec![insn(ebpf::CALL, 0, 0, 0, 300)])); -} - -// Example for InstructionType::Endian. -#[test] -fn test_be32() { - assert_eq!(asm("be32 r1"), Ok(vec![insn(ebpf::BE, 1, 0, 0, 32)])); -} - -// Example for InstructionType::LoadImm. -#[test] -fn test_lddw() { - assert_eq!( - asm("lddw r1, 0x1234abcd5678eeff"), - Ok(vec![ - insn(ebpf::LD_DW_IMM, 1, 0, 0, 0x5678eeff), - insn(0, 0, 0, 0, 0x1234abcd) - ]) - ); - assert_eq!( - asm("lddw r1, 0xff11ee22dd33cc44"), - Ok(vec![ - insn(ebpf::LD_DW_IMM, 1, 0, 0, 0xdd33cc44u32 as i32), - insn(0, 0, 0, 0, 0xff11ee22u32 as i32) - ]) - ); -} - -// Example for InstructionType::LoadAbs. -#[test] -fn test_ldabsw() { - assert_eq!(asm("ldabsw 1"), Ok(vec![insn(ebpf::LD_ABS_W, 0, 0, 0, 1)])); -} - -// Example for InstructionType::LoadInd. -#[test] -fn test_ldindw() { - assert_eq!( - asm("ldindw r1, 2"), - Ok(vec![insn(ebpf::LD_IND_W, 0, 1, 0, 2)]) - ); -} - -// Example for InstructionType::LoadReg. -#[test] -fn test_ldxdw() { - assert_eq!( - asm("ldxdw r1, [r2+3]"), - Ok(vec![insn(ebpf::LD_DW_REG, 1, 2, 3, 0)]) - ); -} - -// Example for InstructionType::StoreImm. -#[test] -fn test_sth() { - assert_eq!( - asm("sth [r1+2], 3"), - Ok(vec![insn(ebpf::ST_H_IMM, 1, 0, 2, 3)]) - ); -} - -// Example for InstructionType::StoreReg. -#[test] -fn test_stxh() { - assert_eq!( - asm("stxh [r1+2], r3"), - Ok(vec![insn(ebpf::ST_H_REG, 1, 3, 2, 0)]) - ); -} - -// Test all supported AluBinary mnemonics. -#[test] -fn test_alu_binary() { - assert_eq!( - asm("add r1, r2 - sub r1, r2 - mul r1, r2 - div r1, r2 - or r1, r2 - and r1, r2 - lsh r1, r2 - rsh r1, r2 - mod r1, r2 - xor r1, r2 - mov r1, r2 - arsh r1, r2"), - Ok(vec![ - insn(ebpf::ADD64_REG, 1, 2, 0, 0), - insn(ebpf::SUB64_REG, 1, 2, 0, 0), - insn(ebpf::MUL64_REG, 1, 2, 0, 0), - insn(ebpf::DIV64_REG, 1, 2, 0, 0), - insn(ebpf::OR64_REG, 1, 2, 0, 0), - insn(ebpf::AND64_REG, 1, 2, 0, 0), - insn(ebpf::LSH64_REG, 1, 2, 0, 0), - insn(ebpf::RSH64_REG, 1, 2, 0, 0), - insn(ebpf::MOD64_REG, 1, 2, 0, 0), - insn(ebpf::XOR64_REG, 1, 2, 0, 0), - insn(ebpf::MOV64_REG, 1, 2, 0, 0), - insn(ebpf::ARSH64_REG, 1, 2, 0, 0) - ]) - ); - - assert_eq!( - asm("add r1, 2 - sub r1, 2 - mul r1, 2 - div r1, 2 - or r1, 2 - and r1, 2 - lsh r1, 2 - rsh r1, 2 - mod r1, 2 - xor r1, 2 - mov r1, 2 - arsh r1, 2"), - Ok(vec![ - insn(ebpf::ADD64_IMM, 1, 0, 0, 2), - insn(ebpf::SUB64_IMM, 1, 0, 0, 2), - insn(ebpf::MUL64_IMM, 1, 0, 0, 2), - insn(ebpf::DIV64_IMM, 1, 0, 0, 2), - insn(ebpf::OR64_IMM, 1, 0, 0, 2), - insn(ebpf::AND64_IMM, 1, 0, 0, 2), - insn(ebpf::LSH64_IMM, 1, 0, 0, 2), - insn(ebpf::RSH64_IMM, 1, 0, 0, 2), - insn(ebpf::MOD64_IMM, 1, 0, 0, 2), - insn(ebpf::XOR64_IMM, 1, 0, 0, 2), - insn(ebpf::MOV64_IMM, 1, 0, 0, 2), - insn(ebpf::ARSH64_IMM, 1, 0, 0, 2) - ]) - ); - - assert_eq!( - asm("add64 r1, r2 - sub64 r1, r2 - mul64 r1, r2 - div64 r1, r2 - or64 r1, r2 - and64 r1, r2 - lsh64 r1, r2 - rsh64 r1, r2 - mod64 r1, r2 - xor64 r1, r2 - mov64 r1, r2 - arsh64 r1, r2"), - Ok(vec![ - insn(ebpf::ADD64_REG, 1, 2, 0, 0), - insn(ebpf::SUB64_REG, 1, 2, 0, 0), - insn(ebpf::MUL64_REG, 1, 2, 0, 0), - insn(ebpf::DIV64_REG, 1, 2, 0, 0), - insn(ebpf::OR64_REG, 1, 2, 0, 0), - insn(ebpf::AND64_REG, 1, 2, 0, 0), - insn(ebpf::LSH64_REG, 1, 2, 0, 0), - insn(ebpf::RSH64_REG, 1, 2, 0, 0), - insn(ebpf::MOD64_REG, 1, 2, 0, 0), - insn(ebpf::XOR64_REG, 1, 2, 0, 0), - insn(ebpf::MOV64_REG, 1, 2, 0, 0), - insn(ebpf::ARSH64_REG, 1, 2, 0, 0) - ]) - ); - - assert_eq!( - asm("add64 r1, 2 - sub64 r1, 2 - mul64 r1, 2 - div64 r1, 2 - or64 r1, 2 - and64 r1, 2 - lsh64 r1, 2 - rsh64 r1, 2 - mod64 r1, 2 - xor64 r1, 2 - mov64 r1, 2 - arsh64 r1, 2"), - Ok(vec![ - insn(ebpf::ADD64_IMM, 1, 0, 0, 2), - insn(ebpf::SUB64_IMM, 1, 0, 0, 2), - insn(ebpf::MUL64_IMM, 1, 0, 0, 2), - insn(ebpf::DIV64_IMM, 1, 0, 0, 2), - insn(ebpf::OR64_IMM, 1, 0, 0, 2), - insn(ebpf::AND64_IMM, 1, 0, 0, 2), - insn(ebpf::LSH64_IMM, 1, 0, 0, 2), - insn(ebpf::RSH64_IMM, 1, 0, 0, 2), - insn(ebpf::MOD64_IMM, 1, 0, 0, 2), - insn(ebpf::XOR64_IMM, 1, 0, 0, 2), - insn(ebpf::MOV64_IMM, 1, 0, 0, 2), - insn(ebpf::ARSH64_IMM, 1, 0, 0, 2) - ]) - ); - - assert_eq!( - asm("add32 r1, r2 - sub32 r1, r2 - mul32 r1, r2 - div32 r1, r2 - or32 r1, r2 - and32 r1, r2 - lsh32 r1, r2 - rsh32 r1, r2 - mod32 r1, r2 - xor32 r1, r2 - mov32 r1, r2 - arsh32 r1, r2"), - Ok(vec![ - insn(ebpf::ADD32_REG, 1, 2, 0, 0), - insn(ebpf::SUB32_REG, 1, 2, 0, 0), - insn(ebpf::MUL32_REG, 1, 2, 0, 0), - insn(ebpf::DIV32_REG, 1, 2, 0, 0), - insn(ebpf::OR32_REG, 1, 2, 0, 0), - insn(ebpf::AND32_REG, 1, 2, 0, 0), - insn(ebpf::LSH32_REG, 1, 2, 0, 0), - insn(ebpf::RSH32_REG, 1, 2, 0, 0), - insn(ebpf::MOD32_REG, 1, 2, 0, 0), - insn(ebpf::XOR32_REG, 1, 2, 0, 0), - insn(ebpf::MOV32_REG, 1, 2, 0, 0), - insn(ebpf::ARSH32_REG, 1, 2, 0, 0) - ]) - ); - - assert_eq!( - asm("add32 r1, 2 - sub32 r1, 2 - mul32 r1, 2 - div32 r1, 2 - or32 r1, 2 - and32 r1, 2 - lsh32 r1, 2 - rsh32 r1, 2 - mod32 r1, 2 - xor32 r1, 2 - mov32 r1, 2 - arsh32 r1, 2"), - Ok(vec![ - insn(ebpf::ADD32_IMM, 1, 0, 0, 2), - insn(ebpf::SUB32_IMM, 1, 0, 0, 2), - insn(ebpf::MUL32_IMM, 1, 0, 0, 2), - insn(ebpf::DIV32_IMM, 1, 0, 0, 2), - insn(ebpf::OR32_IMM, 1, 0, 0, 2), - insn(ebpf::AND32_IMM, 1, 0, 0, 2), - insn(ebpf::LSH32_IMM, 1, 0, 0, 2), - insn(ebpf::RSH32_IMM, 1, 0, 0, 2), - insn(ebpf::MOD32_IMM, 1, 0, 0, 2), - insn(ebpf::XOR32_IMM, 1, 0, 0, 2), - insn(ebpf::MOV32_IMM, 1, 0, 0, 2), - insn(ebpf::ARSH32_IMM, 1, 0, 0, 2) - ]) - ); -} - -// Test all supported AluUnary mnemonics. -#[test] -fn test_alu_unary() { - assert_eq!( - asm("neg r1 - neg64 r1 - neg32 r1"), - Ok(vec![ - insn(ebpf::NEG64, 1, 0, 0, 0), - insn(ebpf::NEG64, 1, 0, 0, 0), - insn(ebpf::NEG32, 1, 0, 0, 0) - ]) - ); -} - -// Test all supported LoadAbs mnemonics. -#[test] -fn test_load_abs() { - assert_eq!( - asm("ldabsw 1 - ldabsh 1 - ldabsb 1 - ldabsdw 1"), - Ok(vec![ - insn(ebpf::LD_ABS_W, 0, 0, 0, 1), - insn(ebpf::LD_ABS_H, 0, 0, 0, 1), - insn(ebpf::LD_ABS_B, 0, 0, 0, 1), - insn(ebpf::LD_ABS_DW, 0, 0, 0, 1) - ]) - ); -} - -// Test all supported LoadInd mnemonics. -#[test] -fn test_load_ind() { - assert_eq!( - asm("ldindw r1, 2 - ldindh r1, 2 - ldindb r1, 2 - ldinddw r1, 2"), - Ok(vec![ - insn(ebpf::LD_IND_W, 0, 1, 0, 2), - insn(ebpf::LD_IND_H, 0, 1, 0, 2), - insn(ebpf::LD_IND_B, 0, 1, 0, 2), - insn(ebpf::LD_IND_DW, 0, 1, 0, 2) - ]) - ); -} - -// Test all supported LoadReg mnemonics. -#[test] -fn test_load_reg() { - assert_eq!( - asm("ldxw r1, [r2+3] - ldxh r1, [r2+3] - ldxb r1, [r2+3] - ldxdw r1, [r2+3]"), - Ok(vec![ - insn(ebpf::LD_W_REG, 1, 2, 3, 0), - insn(ebpf::LD_H_REG, 1, 2, 3, 0), - insn(ebpf::LD_B_REG, 1, 2, 3, 0), - insn(ebpf::LD_DW_REG, 1, 2, 3, 0) - ]) - ); -} - -// Test all supported StoreImm mnemonics. -#[test] -fn test_store_imm() { - assert_eq!( - asm("stw [r1+2], 3 - sth [r1+2], 3 - stb [r1+2], 3 - stdw [r1+2], 3"), - Ok(vec![ - insn(ebpf::ST_W_IMM, 1, 0, 2, 3), - insn(ebpf::ST_H_IMM, 1, 0, 2, 3), - insn(ebpf::ST_B_IMM, 1, 0, 2, 3), - insn(ebpf::ST_DW_IMM, 1, 0, 2, 3) - ]) - ); -} - -// Test all supported StoreReg mnemonics. -#[test] -fn test_store_reg() { - assert_eq!( - asm("stxw [r1+2], r3 - stxh [r1+2], r3 - stxb [r1+2], r3 - stxdw [r1+2], r3"), - Ok(vec![ - insn(ebpf::ST_W_REG, 1, 3, 2, 0), - insn(ebpf::ST_H_REG, 1, 3, 2, 0), - insn(ebpf::ST_B_REG, 1, 3, 2, 0), - insn(ebpf::ST_DW_REG, 1, 3, 2, 0) - ]) - ); -} - -// Test all supported JumpConditional mnemonics. -#[test] -fn test_jump_conditional() { - assert_eq!( - asm("jeq r1, r2, +3 - jgt r1, r2, +3 - jge r1, r2, +3 - jlt r1, r2, +3 - jle r1, r2, +3 - jset r1, r2, +3 - jne r1, r2, +3 - jsgt r1, r2, +3 - jsge r1, r2, +3 - jslt r1, r2, +3 - jsle r1, r2, +3"), - Ok(vec![ - insn(ebpf::JEQ_REG, 1, 2, 3, 0), - insn(ebpf::JGT_REG, 1, 2, 3, 0), - insn(ebpf::JGE_REG, 1, 2, 3, 0), - insn(ebpf::JLT_REG, 1, 2, 3, 0), - insn(ebpf::JLE_REG, 1, 2, 3, 0), - insn(ebpf::JSET_REG, 1, 2, 3, 0), - insn(ebpf::JNE_REG, 1, 2, 3, 0), - insn(ebpf::JSGT_REG, 1, 2, 3, 0), - insn(ebpf::JSGE_REG, 1, 2, 3, 0), - insn(ebpf::JSLT_REG, 1, 2, 3, 0), - insn(ebpf::JSLE_REG, 1, 2, 3, 0) - ]) - ); - - assert_eq!( - asm("jeq r1, 2, +3 - jgt r1, 2, +3 - jge r1, 2, +3 - jlt r1, 2, +3 - jle r1, 2, +3 - jset r1, 2, +3 - jne r1, 2, +3 - jsgt r1, 2, +3 - jsge r1, 2, +3 - jslt r1, 2, +3 - jsle r1, 2, +3"), - Ok(vec![ - insn(ebpf::JEQ_IMM, 1, 0, 3, 2), - insn(ebpf::JGT_IMM, 1, 0, 3, 2), - insn(ebpf::JGE_IMM, 1, 0, 3, 2), - insn(ebpf::JLT_IMM, 1, 0, 3, 2), - insn(ebpf::JLE_IMM, 1, 0, 3, 2), - insn(ebpf::JSET_IMM, 1, 0, 3, 2), - insn(ebpf::JNE_IMM, 1, 0, 3, 2), - insn(ebpf::JSGT_IMM, 1, 0, 3, 2), - insn(ebpf::JSGE_IMM, 1, 0, 3, 2), - insn(ebpf::JSLT_IMM, 1, 0, 3, 2), - insn(ebpf::JSLE_IMM, 1, 0, 3, 2) - ]) - ); - - assert_eq!( - asm("jeq32 r1, r2, +3 - jgt32 r1, r2, +3 - jge32 r1, r2, +3 - jlt32 r1, r2, +3 - jle32 r1, r2, +3 - jset32 r1, r2, +3 - jne32 r1, r2, +3 - jsgt32 r1, r2, +3 - jsge32 r1, r2, +3 - jslt32 r1, r2, +3 - jsle32 r1, r2, +3"), - Ok(vec![ - insn(ebpf::JEQ_REG32, 1, 2, 3, 0), - insn(ebpf::JGT_REG32, 1, 2, 3, 0), - insn(ebpf::JGE_REG32, 1, 2, 3, 0), - insn(ebpf::JLT_REG32, 1, 2, 3, 0), - insn(ebpf::JLE_REG32, 1, 2, 3, 0), - insn(ebpf::JSET_REG32, 1, 2, 3, 0), - insn(ebpf::JNE_REG32, 1, 2, 3, 0), - insn(ebpf::JSGT_REG32, 1, 2, 3, 0), - insn(ebpf::JSGE_REG32, 1, 2, 3, 0), - insn(ebpf::JSLT_REG32, 1, 2, 3, 0), - insn(ebpf::JSLE_REG32, 1, 2, 3, 0) - ]) - ); - - assert_eq!( - asm("jeq32 r1, 2, +3 - jgt32 r1, 2, +3 - jge32 r1, 2, +3 - jlt32 r1, 2, +3 - jle32 r1, 2, +3 - jset32 r1, 2, +3 - jne32 r1, 2, +3 - jsgt32 r1, 2, +3 - jsge32 r1, 2, +3 - jslt32 r1, 2, +3 - jsle32 r1, 2, +3"), - Ok(vec![ - insn(ebpf::JEQ_IMM32, 1, 0, 3, 2), - insn(ebpf::JGT_IMM32, 1, 0, 3, 2), - insn(ebpf::JGE_IMM32, 1, 0, 3, 2), - insn(ebpf::JLT_IMM32, 1, 0, 3, 2), - insn(ebpf::JLE_IMM32, 1, 0, 3, 2), - insn(ebpf::JSET_IMM32, 1, 0, 3, 2), - insn(ebpf::JNE_IMM32, 1, 0, 3, 2), - insn(ebpf::JSGT_IMM32, 1, 0, 3, 2), - insn(ebpf::JSGE_IMM32, 1, 0, 3, 2), - insn(ebpf::JSLT_IMM32, 1, 0, 3, 2), - insn(ebpf::JSLE_IMM32, 1, 0, 3, 2) - ]) - ); -} - -// Test all supported Endian mnemonics. -#[test] -fn test_endian() { - assert_eq!( - asm("be16 r1 - be32 r1 - be64 r1 - le16 r1 - le32 r1 - le64 r1"), - Ok(vec![ - insn(ebpf::BE, 1, 0, 0, 16), - insn(ebpf::BE, 1, 0, 0, 32), - insn(ebpf::BE, 1, 0, 0, 64), - insn(ebpf::LE, 1, 0, 0, 16), - insn(ebpf::LE, 1, 0, 0, 32), - insn(ebpf::LE, 1, 0, 0, 64) - ]) - ); -} - -#[test] -fn test_large_immediate() { - assert_eq!( - asm("add64 r1, 2147483647"), - Ok(vec![insn(ebpf::ADD64_IMM, 1, 0, 0, 2147483647)]) - ); - assert_eq!( - asm("add64 r1, -2147483648"), - Ok(vec![insn(ebpf::ADD64_IMM, 1, 0, 0, -2147483648)]) - ); -} - -#[test] -fn test_tcp_sack() { - assert_eq!(assemble(TCP_SACK_ASM), Ok(TCP_SACK_BIN.to_vec())); -} - -#[test] -fn test_error_invalid_instruction() { - assert_eq!(asm("abcd"), Err("Invalid instruction \"abcd\"".to_string())); -} - -#[test] -fn test_error_unexpected_operands() { - assert_eq!( - asm("add 1, 2"), - Err("Failed to encode add: Unexpected operands: [Integer(1), Integer(2)]".to_string()) - ); -} - -#[test] -fn test_error_too_many_operands() { - assert_eq!( - asm("add 1, 2, 3, 4"), - Err("Failed to encode add: Too many operands".to_string()) - ); -} - -#[test] -fn test_error_operands_out_of_range() { - assert_eq!( - asm("add r16, r2"), - Err("Failed to encode add: Invalid destination register 16".to_string()) - ); - assert_eq!( - asm("add r1, r16"), - Err("Failed to encode add: Invalid source register 16".to_string()) - ); - assert_eq!( - asm("ja -32769"), - Err("Failed to encode ja: Invalid offset -32769".to_string()) - ); - assert_eq!( - asm("ja 32768"), - Err("Failed to encode ja: Invalid offset 32768".to_string()) - ); - assert_eq!( - asm("add r1, 4294967296"), - Err("Failed to encode add: Invalid immediate 4294967296".to_string()) - ); - assert_eq!( - asm("add r1, 2147483648"), - Err("Failed to encode add: Invalid immediate 2147483648".to_string()) - ); - assert_eq!( - asm("add r1, -2147483649"), - Err("Failed to encode add: Invalid immediate -2147483649".to_string()) - ); -} diff --git a/kernel/crates/rbpf/tests/common.rs b/kernel/crates/rbpf/tests/common.rs deleted file mode 100644 index e3783991..00000000 --- a/kernel/crates/rbpf/tests/common.rs +++ /dev/null @@ -1,97 +0,0 @@ -// SPDX-License-Identifier: (Apache-2.0 OR MIT) -// Converted from the tests for uBPF -// Copyright 2015 Big Switch Networks, Inc -// Copyright 2016 6WIND S.A. - -// Assembly code and data for tcp_sack testcases. - -#[allow(dead_code)] -pub const TCP_SACK_ASM: &str = " - ldxb r2, [r1+12] - ldxb r3, [r1+13] - lsh r3, 0x8 - or r3, r2 - mov r0, 0x0 - jne r3, 0x8, +37 - ldxb r2, [r1+23] - jne r2, 0x6, +35 - ldxb r2, [r1+14] - add r1, 0xe - and r2, 0xf - lsh r2, 0x2 - add r1, r2 - mov r0, 0x0 - ldxh r4, [r1+12] - add r1, 0x14 - rsh r4, 0x2 - and r4, 0x3c - mov r2, r4 - add r2, -20 - mov r5, 0x15 - mov r3, 0x0 - jgt r5, r4, +20 - mov r5, r3 - lsh r5, 0x20 - arsh r5, 0x20 - mov r4, r1 - add r4, r5 - ldxb r5, [r4] - jeq r5, 0x1, +4 - jeq r5, 0x0, +12 - mov r6, r3 - jeq r5, 0x5, +9 - ja +2 - add r3, 0x1 - mov r6, r3 - ldxb r3, [r4+1] - add r3, r6 - lsh r3, 0x20 - arsh r3, 0x20 - jsgt r2, r3, -18 - ja +1 - mov r0, 0x1 - exit"; - -#[allow(dead_code)] -pub const TCP_SACK_BIN: [u8; 352] = [ - 0x71, 0x12, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x71, 0x13, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x67, 0x03, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x4f, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x03, 0x25, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x71, 0x12, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x02, 0x23, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x71, 0x12, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x01, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, - 0x57, 0x02, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x67, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x0f, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x69, 0x14, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x01, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, - 0x77, 0x04, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x57, 0x04, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, - 0xbf, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x02, 0x00, 0x00, 0xec, 0xff, 0xff, 0xff, - 0xb7, 0x05, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0xb7, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x2d, 0x45, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbf, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x67, 0x05, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0xc7, 0x05, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0xbf, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x71, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x05, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x15, 0x05, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbf, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x15, 0x05, 0x09, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x07, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xbf, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x71, 0x43, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x67, 0x03, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0xc7, 0x03, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x6d, 0x32, 0xee, 0xff, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xb7, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -]; - -#[allow(dead_code)] -pub const TCP_SACK_MATCH: [u8; 78] = [ - 0x00, 0x26, 0x62, 0x2f, 0x47, 0x87, 0x00, 0x1d, 0x60, 0xb3, 0x01, 0x84, 0x08, 0x00, 0x45, 0x00, - 0x00, 0x40, 0xa8, 0xde, 0x40, 0x00, 0x40, 0x06, 0x9d, 0x58, 0xc0, 0xa8, 0x01, 0x03, 0x3f, 0x74, - 0xf3, 0x61, 0xe5, 0xc0, 0x00, 0x50, 0xe5, 0x94, 0x3f, 0x77, 0xa3, 0xc4, 0xc4, 0x80, 0xb0, 0x10, - 0x01, 0x3e, 0x34, 0xb6, 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a, 0x00, 0x17, 0x95, 0x6f, 0x8d, 0x9d, - 0x9e, 0x27, 0x01, 0x01, 0x05, 0x0a, 0xa3, 0xc4, 0xca, 0x28, 0xa3, 0xc4, 0xcf, 0xd0, -]; - -#[allow(dead_code)] -pub const TCP_SACK_NOMATCH: [u8; 66] = [ - 0x00, 0x26, 0x62, 0x2f, 0x47, 0x87, 0x00, 0x1d, 0x60, 0xb3, 0x01, 0x84, 0x08, 0x00, 0x45, 0x00, - 0x00, 0x40, 0xa8, 0xde, 0x40, 0x00, 0x40, 0x06, 0x9d, 0x58, 0xc0, 0xa8, 0x01, 0x03, 0x3f, 0x74, - 0xf3, 0x61, 0xe5, 0xc0, 0x00, 0x50, 0xe5, 0x94, 0x3f, 0x77, 0xa3, 0xc4, 0xc4, 0x80, 0x80, 0x10, - 0x01, 0x3e, 0x34, 0xb6, 0x00, 0x00, 0x01, 0x01, 0x08, 0x0a, 0x00, 0x17, 0x95, 0x6f, 0x8d, 0x9d, - 0x9e, 0x27, -]; diff --git a/kernel/crates/rbpf/tests/cranelift.rs b/kernel/crates/rbpf/tests/cranelift.rs deleted file mode 100644 index 19aca973..00000000 --- a/kernel/crates/rbpf/tests/cranelift.rs +++ /dev/null @@ -1,2257 +0,0 @@ -// SPDX-License-Identifier: (Apache-2.0 OR MIT) - -#![allow(clippy::unreadable_literal)] -#![cfg(feature = "cranelift")] - -extern crate rbpf; -mod common; - -use rbpf::{assembler::assemble, helpers}; - -use crate::common::{TCP_SACK_ASM, TCP_SACK_MATCH, TCP_SACK_NOMATCH}; - -macro_rules! test_cranelift { - ($name:ident, $prog:expr, $expected:expr) => { - #[test] - fn $name() { - let prog = assemble($prog).unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.cranelift_compile().unwrap(); - assert_eq!(vm.execute_program_cranelift().unwrap(), $expected); - } - }; - ($name:ident, $prog:expr, $mem:expr, $expected:expr) => { - #[test] - fn $name() { - let prog = assemble($prog).unwrap(); - let mem = &mut $mem; - let mut vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - vm.cranelift_compile().unwrap(); - assert_eq!(vm.execute_program_cranelift(mem).unwrap(), $expected); - } - }; -} - -test_cranelift!( - test_cranelift_add, - " - mov32 r0, 0 - mov32 r1, 2 - add32 r0, 1 - add32 r0, r1 - exit - ", - 0x3 -); - -test_cranelift!( - test_cranelift_alu64_arith, - " - mov r0, 0 - mov r1, 1 - mov r2, 2 - mov r3, 3 - mov r4, 4 - mov r5, 5 - mov r6, 6 - mov r7, 7 - mov r8, 8 - mov r9, 9 - add r0, 23 - add r0, r7 - sub r0, 13 - sub r0, r1 - mul r0, 7 - mul r0, r3 - div r0, 2 - div r0, r4 - exit - ", - 0x2a -); - -test_cranelift!( - test_cranelift_alu64_bit, - " - mov r0, 0 - mov r1, 1 - mov r2, 2 - mov r3, 3 - mov r4, 4 - mov r5, 5 - mov r6, 6 - mov r7, 7 - mov r8, 8 - or r0, r5 - or r0, 0xa0 - and r0, 0xa3 - mov r9, 0x91 - and r0, r9 - lsh r0, 32 - lsh r0, 22 - lsh r0, r8 - rsh r0, 32 - rsh r0, 19 - rsh r0, r7 - xor r0, 0x03 - xor r0, r2 - exit - ", - 0x11 -); - -test_cranelift!( - test_cranelift_alu_arith, - " - mov32 r0, 0 - mov32 r1, 1 - mov32 r2, 2 - mov32 r3, 3 - mov32 r4, 4 - mov32 r5, 5 - mov32 r6, 6 - mov32 r7, 7 - mov32 r8, 8 - mov32 r9, 9 - add32 r0, 23 - add32 r0, r7 - sub32 r0, 13 - sub32 r0, r1 - mul32 r0, 7 - mul32 r0, r3 - div32 r0, 2 - div32 r0, r4 - exit - ", - 0x2a -); - -test_cranelift!( - test_cranelift_alu_bit, - " - mov32 r0, 0 - mov32 r1, 1 - mov32 r2, 2 - mov32 r3, 3 - mov32 r4, 4 - mov32 r5, 5 - mov32 r6, 6 - mov32 r7, 7 - mov32 r8, 8 - or32 r0, r5 - or32 r0, 0xa0 - and32 r0, 0xa3 - mov32 r9, 0x91 - and32 r0, r9 - lsh32 r0, 22 - lsh32 r0, r8 - rsh32 r0, 19 - rsh32 r0, r7 - xor32 r0, 0x03 - xor32 r0, r2 - exit - ", - 0x11 -); - -test_cranelift!( - test_cranelift_arsh32_high_shift, - " - mov r0, 8 - lddw r1, 0x100000001 - arsh32 r0, r1 - exit - ", - 0x4 -); - -test_cranelift!( - test_cranelift_arsh, - " - mov32 r0, 0xf8 - lsh32 r0, 28 - arsh32 r0, 16 - exit - ", - 0xffff8000 -); - -test_cranelift!( - test_cranelift_arsh64, - " - mov32 r0, 1 - lsh r0, 63 - arsh r0, 55 - mov32 r1, 5 - arsh r0, r1 - exit - ", - 0xfffffffffffffff8 -); - -test_cranelift!( - test_cranelift_arsh_reg, - " - mov32 r0, 0xf8 - mov32 r1, 16 - lsh32 r0, 28 - arsh32 r0, r1 - exit - ", - 0xffff8000 -); - -test_cranelift!( - test_cranelift_be16, - " - ldxh r0, [r1] - be16 r0 - exit - ", - [0x11, 0x22], - 0x1122 -); - -test_cranelift!( - test_cranelift_be16_high, - " - ldxdw r0, [r1] - be16 r0 - exit - ", - [0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88], - 0x1122 -); - -test_cranelift!( - test_cranelift_be32, - " - ldxw r0, [r1] - be32 r0 - exit - ", - [0x11, 0x22, 0x33, 0x44], - 0x11223344 -); - -test_cranelift!( - test_cranelift_be32_high, - " - ldxdw r0, [r1] - be32 r0 - exit - ", - [0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88], - 0x11223344 -); - -test_cranelift!( - test_cranelift_be64, - " - ldxdw r0, [r1] - be64 r0 - exit - ", - [0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88], - 0x1122334455667788 -); - -#[test] -fn test_cranelift_call() { - let prog = assemble( - " - mov r1, 1 - mov r2, 2 - mov r3, 3 - mov r4, 4 - mov r5, 5 - call 0 - exit", - ) - .unwrap(); - - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.register_helper(0, helpers::gather_bytes).unwrap(); - vm.cranelift_compile().unwrap(); - assert_eq!(vm.execute_program_cranelift().unwrap(), 0x0102030405); -} - -#[test] -#[should_panic(expected = "[CRANELIFT] Error: unknown helper function (id: 0x3f)")] -fn test_cranelift_err_call_unreg() { - let prog = assemble( - " - mov r1, 1 - mov r2, 2 - mov r3, 3 - mov r4, 4 - mov r5, 5 - call 63 - exit - ", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.cranelift_compile().unwrap(); -} - -#[test] -fn test_cranelift_call_memfrob() { - let prog = assemble( - " - mov r6, r1 - add r1, 2 - mov r2, 4 - call 1 - ldxdw r0, [r6] - be64 r0 - exit", - ) - .unwrap(); - - let mut vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - vm.register_helper(1, helpers::memfrob).unwrap(); - let mem = &mut [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]; - vm.cranelift_compile().unwrap(); - assert_eq!( - vm.execute_program_cranelift(mem).unwrap(), - 0x102292e2f2c0708 - ); -} - -test_cranelift!( - test_cranelift_div32_high_divisor, - " - mov r0, 12 - lddw r1, 0x100000004 - div32 r0, r1 - exit - ", - 0x3 -); - -test_cranelift!( - test_cranelift_div32_imm, - " - lddw r0, 0x10000000c - div32 r0, 4 - exit - ", - 0x3 -); - -test_cranelift!( - test_cranelift_div32_reg, - " - lddw r0, 0x10000000c - mov r1, 4 - div32 r0, r1 - exit - ", - 0x3 -); - -test_cranelift!( - test_cranelift_div64_imm, - " - mov r0, 0xc - lsh r0, 32 - div r0, 4 - exit - ", - 0x300000000 -); - -test_cranelift!( - test_cranelift_div64_reg, - " - mov r0, 0xc - lsh r0, 32 - mov r1, 4 - div r0, r1 - exit - ", - 0x300000000 -); - -test_cranelift!( - test_cranelift_early_exit, - " - mov r0, 3 - exit - mov r0, 4 - exit - ", - 0x3 -); - -test_cranelift!( - test_cranelift_div64_by_zero_imm, - " - mov32 r0, 1 - div r0, 0 - exit - ", - 0x0 -); - -test_cranelift!( - test_cranelift_div_by_zero_imm, - " - mov32 r0, 1 - div32 r0, 0 - exit - ", - 0x0 -); - -test_cranelift!( - test_cranelift_mod64_by_zero_imm, - " - mov32 r0, 1 - mod r0, 0 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_mod_by_zero_imm, - " - mov32 r0, 1 - mod32 r0, 0 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_div64_by_zero_reg, - " - mov32 r0, 1 - mov32 r1, 0 - div r0, r1 - exit - ", - 0x0 -); - -test_cranelift!( - test_cranelift_div_by_zero_reg, - " - mov32 r0, 1 - mov32 r1, 0 - div32 r0, r1 - exit - ", - 0x0 -); - -test_cranelift!( - test_cranelift_mod64_by_zero_reg, - " - mov32 r0, 1 - mov32 r1, 0 - mod r0, r1 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_mod_by_zero_reg, - " - mov32 r0, 1 - mov32 r1, 0 - mod32 r0, r1 - exit - ", - 0x1 -); - -#[test] -// #[should_panic(expected = "Error: out of bounds memory store (insn #1)")] -#[ignore = "We have stack OOB checks, but we don't yet catch the trap code and convert it into a panic"] -fn test_cranelift_err_stack_out_of_bound() { - let prog = [ - 0x72, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, - ]; - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.cranelift_compile().unwrap(); - vm.execute_program_cranelift().unwrap(); -} - -test_cranelift!( - test_cranelift_exit, - " - mov r0, 0 - exit - ", - 0x0 -); - -test_cranelift!( - test_cranelift_ja, - " - mov r0, 1 - ja +1 - mov r0, 2 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_jeq_imm, - " - mov32 r0, 0 - mov32 r1, 0xa - jeq r1, 0xb, +4 - mov32 r0, 1 - mov32 r1, 0xb - jeq r1, 0xb, +1 - mov32 r0, 2 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_jeq_reg, - " - mov32 r0, 0 - mov32 r1, 0xa - mov32 r2, 0xb - jeq r1, r2, +4 - mov32 r0, 1 - mov32 r1, 0xb - jeq r1, r2, +1 - mov32 r0, 2 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_jge_imm, - " - mov32 r0, 0 - mov32 r1, 0xa - jge r1, 0xb, +4 - mov32 r0, 1 - mov32 r1, 0xc - jge r1, 0xb, +1 - mov32 r0, 2 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_jle_imm, - " - mov32 r0, 0 - mov32 r1, 5 - jle r1, 4, +1 - jle r1, 6, +1 - exit - jle r1, 5, +1 - exit - mov32 r0, 1 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_jle_reg, - " - mov r0, 0 - mov r1, 5 - mov r2, 4 - mov r3, 6 - jle r1, r2, +2 - jle r1, r1, +1 - exit - jle r1, r3, +1 - exit - mov r0, 1 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_jgt_imm, - " - mov32 r0, 0 - mov32 r1, 5 - jgt r1, 6, +2 - jgt r1, 5, +1 - jgt r1, 4, +1 - exit - mov32 r0, 1 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_jgt_reg, - " - mov r0, 0 - mov r1, 5 - mov r2, 6 - mov r3, 4 - jgt r1, r2, +2 - jgt r1, r1, +1 - jgt r1, r3, +1 - exit - mov r0, 1 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_jlt_imm, - " - mov32 r0, 0 - mov32 r1, 5 - jlt r1, 4, +2 - jlt r1, 5, +1 - jlt r1, 6, +1 - exit - mov32 r0, 1 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_jlt_reg, - " - mov r0, 0 - mov r1, 5 - mov r2, 4 - mov r3, 6 - jlt r1, r2, +2 - jlt r1, r1, +1 - jlt r1, r3, +1 - exit - mov r0, 1 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_jit_bounce, - " - mov r0, 1 - mov r6, r0 - mov r7, r6 - mov r8, r7 - mov r9, r8 - mov r0, r9 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_jne_reg, - " - mov32 r0, 0 - mov32 r1, 0xb - mov32 r2, 0xb - jne r1, r2, +4 - mov32 r0, 1 - mov32 r1, 0xa - jne r1, r2, +1 - mov32 r0, 2 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_jset_imm, - " - mov32 r0, 0 - mov32 r1, 0x7 - jset r1, 0x8, +4 - mov32 r0, 1 - mov32 r1, 0x9 - jset r1, 0x8, +1 - mov32 r0, 2 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_jset_reg, - " - mov32 r0, 0 - mov32 r1, 0x7 - mov32 r2, 0x8 - jset r1, r2, +4 - mov32 r0, 1 - mov32 r1, 0x9 - jset r1, r2, +1 - mov32 r0, 2 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_jsge_imm, - " - mov32 r0, 0 - mov r1, -2 - jsge r1, -1, +5 - jsge r1, 0, +4 - mov32 r0, 1 - mov r1, -1 - jsge r1, -1, +1 - mov32 r0, 2 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_jsge_reg, - " - mov32 r0, 0 - mov r1, -2 - mov r2, -1 - mov32 r3, 0 - jsge r1, r2, +5 - jsge r1, r3, +4 - mov32 r0, 1 - mov r1, r2 - jsge r1, r2, +1 - mov32 r0, 2 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_jsle_imm, - " - mov32 r0, 0 - mov r1, -2 - jsle r1, -3, +1 - jsle r1, -1, +1 - exit - mov32 r0, 1 - jsle r1, -2, +1 - mov32 r0, 2 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_jsle_reg, - " - mov32 r0, 0 - mov r1, -1 - mov r2, -2 - mov32 r3, 0 - jsle r1, r2, +1 - jsle r1, r3, +1 - exit - mov32 r0, 1 - mov r1, r2 - jsle r1, r2, +1 - mov32 r0, 2 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_jsgt_imm, - " - mov32 r0, 0 - mov r1, -2 - jsgt r1, -1, +4 - mov32 r0, 1 - mov32 r1, 0 - jsgt r1, -1, +1 - mov32 r0, 2 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_jsgt_reg, - " - mov32 r0, 0 - mov r1, -2 - mov r2, -1 - jsgt r1, r2, +4 - mov32 r0, 1 - mov32 r1, 0 - jsgt r1, r2, +1 - mov32 r0, 2 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_jslt_imm, - " - mov32 r0, 0 - mov r1, -2 - jslt r1, -3, +2 - jslt r1, -2, +1 - jslt r1, -1, +1 - exit - mov32 r0, 1 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_jslt_reg, - " - mov32 r0, 0 - mov r1, -2 - mov r2, -3 - mov r3, -1 - jslt r1, r1, +2 - jslt r1, r2, +1 - jslt r1, r3, +1 - exit - mov32 r0, 1 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_jeq32_imm, - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0x0 - mov32 r1, 0xa - jeq32 r1, 0xb, +5 - mov32 r0, 1 - mov r1, 0xb - or r1, r9 - jeq32 r1, 0xb, +1 - mov32 r0, 2 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_jeq32_reg, - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, 0xa - mov32 r2, 0xb - jeq32 r1, r2, +5 - mov32 r0, 1 - mov32 r1, 0xb - or r1, r9 - jeq32 r1, r2, +1 - mov32 r0, 2 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_jge32_imm, - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, 0xa - jge32 r1, 0xb, +5 - mov32 r0, 1 - or r1, r9 - mov32 r1, 0xc - jge32 r1, 0xb, +1 - mov32 r0, 2 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_jge32_reg, - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, 0xa - mov32 r2, 0xb - jge32 r1, r2, +5 - mov32 r0, 1 - or r1, r9 - mov32 r1, 0xc - jge32 r1, r2, +1 - mov32 r0, 2 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_jgt32_imm, - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, 5 - or r1, r9 - jgt32 r1, 6, +4 - jgt32 r1, 5, +3 - jgt32 r1, 4, +1 - exit - mov32 r0, 1 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_jgt32_reg, - " - mov r9, 1 - lsh r9, 32 - mov r0, 0 - mov r1, 5 - mov32 r1, 5 - or r1, r9 - mov r2, 6 - mov r3, 4 - jgt32 r1, r2, +4 - jgt32 r1, r1, +3 - jgt32 r1, r3, +1 - exit - mov r0, 1 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_jle32_imm, - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, 5 - or r1, r9 - jle32 r1, 4, +5 - jle32 r1, 6, +1 - exit - jle32 r1, 5, +1 - exit - mov32 r0, 1 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_jle32_reg, - " - mov r9, 1 - lsh r9, 32 - mov r0, 0 - mov r1, 5 - mov r2, 4 - mov r3, 6 - or r1, r9 - jle32 r1, r2, +5 - jle32 r1, r1, +1 - exit - jle32 r1, r3, +1 - exit - mov r0, 1 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_jlt32_imm, - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, 5 - or r1, r9 - jlt32 r1, 4, +4 - jlt32 r1, 5, +3 - jlt32 r1, 6, +1 - exit - mov32 r0, 1 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_jlt32_reg, - " - mov r9, 1 - lsh r9, 32 - mov r0, 0 - mov r1, 5 - mov r2, 4 - mov r3, 6 - or r1, r9 - jlt32 r1, r2, +4 - jlt32 r1, r1, +3 - jlt32 r1, r3, +1 - exit - mov r0, 1 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_jne32_imm, - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, 0xb - or r1, r9 - jne32 r1, 0xb, +4 - mov32 r0, 1 - mov32 r1, 0xa - or r1, r9 - jne32 r1, 0xb, +1 - mov32 r0, 2 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_jne32_reg, - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, 0xb - or r1, r9 - mov32 r2, 0xb - jne32 r1, r2, +4 - mov32 r0, 1 - mov32 r1, 0xa - or r1, r9 - jne32 r1, r2, +1 - mov32 r0, 2 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_jset32_imm, - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, 0x7 - or r1, r9 - jset32 r1, 0x8, +4 - mov32 r0, 1 - mov32 r1, 0x9 - jset32 r1, 0x8, +1 - mov32 r0, 2 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_jset32_reg, - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, 0x7 - or r1, r9 - mov32 r2, 0x8 - jset32 r1, r2, +4 - mov32 r0, 1 - mov32 r1, 0x9 - jset32 r1, r2, +1 - mov32 r0, 2 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_jsge32_imm, - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, -2 - or r1, r9 - jsge32 r1, -1, +5 - jsge32 r1, 0, +4 - mov32 r0, 1 - mov r1, -1 - jsge32 r1, -1, +1 - mov32 r0, 2 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_jsge32_reg, - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, -2 - or r1, r9 - mov r2, -1 - mov32 r3, 0 - jsge32 r1, r2, +5 - jsge32 r1, r3, +4 - mov32 r0, 1 - mov r1, r2 - jsge32 r1, r2, +1 - mov32 r0, 2 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_jsgt32_imm, - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, -2 - or r1, r9 - jsgt32 r1, -1, +4 - mov32 r0, 1 - mov32 r1, 0 - jsgt32 r1, -1, +1 - mov32 r0, 2 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_jsgt32_reg, - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, -2 - or r1, r9 - mov r2, -1 - jsgt32 r1, r2, +4 - mov32 r0, 1 - mov32 r1, 0 - jsgt32 r1, r2, +1 - mov32 r0, 2 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_jsle32_imm, - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, -2 - or r1, r9 - jsle32 r1, -3, +5 - jsle32 r1, -1, +1 - exit - mov32 r0, 1 - jsle32 r1, -2, +1 - mov32 r0, 2 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_jsle32_reg, - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, -2 - or r1, r9 - mov r2, -3 - mov32 r3, 0 - jsle32 r1, r2, +6 - jsle32 r1, r3, +1 - exit - mov32 r0, 1 - mov r1, r2 - jsle32 r1, r2, +1 - mov32 r0, 2 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_jslt32_imm, - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, -2 - or r1, r9 - jslt32 r1, -3, +4 - jslt32 r1, -2, +3 - jslt32 r1, -1, +1 - exit - mov32 r0, 1 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_jslt32_reg, - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, -2 - or r1, r9 - mov r2, -3 - mov r3, -1 - jslt32 r1, r1, +4 - jslt32 r1, r2, +3 - jslt32 r1, r3, +1 - exit - mov32 r0, 1 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_lddw, - " - lddw r0, 0x1122334455667788 - exit - ", - 0x1122334455667788 -); - -test_cranelift!( - test_cranelift_lddw2, - " - lddw r0, 0x0000000080000000 - exit - ", - 0x80000000 -); - -test_cranelift!( - test_cranelift_ldxb_all, - " - mov r0, r1 - ldxb r9, [r0+0] - lsh r9, 0 - ldxb r8, [r0+1] - lsh r8, 4 - ldxb r7, [r0+2] - lsh r7, 8 - ldxb r6, [r0+3] - lsh r6, 12 - ldxb r5, [r0+4] - lsh r5, 16 - ldxb r4, [r0+5] - lsh r4, 20 - ldxb r3, [r0+6] - lsh r3, 24 - ldxb r2, [r0+7] - lsh r2, 28 - ldxb r1, [r0+8] - lsh r1, 32 - ldxb r0, [r0+9] - lsh r0, 36 - or r0, r1 - or r0, r2 - or r0, r3 - or r0, r4 - or r0, r5 - or r0, r6 - or r0, r7 - or r0, r8 - or r0, r9 - exit - ", - [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09], - 0x9876543210 -); - -test_cranelift!( - test_cranelift_ldxb, - " - ldxb r0, [r1+2] - exit - ", - [0xaa, 0xbb, 0x11, 0xcc, 0xdd], - 0x11 -); - -test_cranelift!( - test_cranelift_ldxdw, - " - ldxdw r0, [r1+2] - exit - ", - [0xaa, 0xbb, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0xcc, 0xdd], - 0x8877665544332211 -); - -test_cranelift!( - test_cranelift_ldxh_all, - " - mov r0, r1 - ldxh r9, [r0+0] - be16 r9 - lsh r9, 0 - ldxh r8, [r0+2] - be16 r8 - lsh r8, 4 - ldxh r7, [r0+4] - be16 r7 - lsh r7, 8 - ldxh r6, [r0+6] - be16 r6 - lsh r6, 12 - ldxh r5, [r0+8] - be16 r5 - lsh r5, 16 - ldxh r4, [r0+10] - be16 r4 - lsh r4, 20 - ldxh r3, [r0+12] - be16 r3 - lsh r3, 24 - ldxh r2, [r0+14] - be16 r2 - lsh r2, 28 - ldxh r1, [r0+16] - be16 r1 - lsh r1, 32 - ldxh r0, [r0+18] - be16 r0 - lsh r0, 36 - or r0, r1 - or r0, r2 - or r0, r3 - or r0, r4 - or r0, r5 - or r0, r6 - or r0, r7 - or r0, r8 - or r0, r9 - exit - ", - [ - 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x00, - 0x07, 0x00, 0x08, 0x00, 0x09 - ], - 0x9876543210 -); - -test_cranelift!( - test_cranelift_ldxh_all2, - " - mov r0, r1 - ldxh r9, [r0+0] - be16 r9 - ldxh r8, [r0+2] - be16 r8 - ldxh r7, [r0+4] - be16 r7 - ldxh r6, [r0+6] - be16 r6 - ldxh r5, [r0+8] - be16 r5 - ldxh r4, [r0+10] - be16 r4 - ldxh r3, [r0+12] - be16 r3 - ldxh r2, [r0+14] - be16 r2 - ldxh r1, [r0+16] - be16 r1 - ldxh r0, [r0+18] - be16 r0 - or r0, r1 - or r0, r2 - or r0, r3 - or r0, r4 - or r0, r5 - or r0, r6 - or r0, r7 - or r0, r8 - or r0, r9 - exit - ", - [ - 0x00, 0x01, 0x00, 0x02, 0x00, 0x04, 0x00, 0x08, 0x00, 0x10, 0x00, 0x20, 0x00, 0x40, 0x00, - 0x80, 0x01, 0x00, 0x02, 0x00 - ], - 0x3ff -); - -test_cranelift!( - test_cranelift_ldxh, - " - ldxh r0, [r1+2] - exit - ", - [0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd], - 0x2211 -); - -test_cranelift!( - test_cranelift_ldxh_same_reg, - " - mov r0, r1 - sth [r0], 0x1234 - ldxh r0, [r0] - exit - ", - [0xff, 0xff], - 0x1234 -); - -test_cranelift!( - test_cranelift_ldxw_all, - " - mov r0, r1 - ldxw r9, [r0+0] - be32 r9 - ldxw r8, [r0+4] - be32 r8 - ldxw r7, [r0+8] - be32 r7 - ldxw r6, [r0+12] - be32 r6 - ldxw r5, [r0+16] - be32 r5 - ldxw r4, [r0+20] - be32 r4 - ldxw r3, [r0+24] - be32 r3 - ldxw r2, [r0+28] - be32 r2 - ldxw r1, [r0+32] - be32 r1 - ldxw r0, [r0+36] - be32 r0 - or r0, r1 - or r0, r2 - or r0, r3 - or r0, r4 - or r0, r5 - or r0, r6 - or r0, r7 - or r0, r8 - or r0, r9 - exit - ", - [ - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00 - ], - 0x030f0f -); - -test_cranelift!( - test_cranelift_ldxw, - " - ldxw r0, [r1+2] - exit - ", - [0xaa, 0xbb, 0x11, 0x22, 0x33, 0x44, 0xcc, 0xdd], - 0x44332211 -); - -test_cranelift!( - test_cranelift_le16, - " - ldxh r0, [r1] - le16 r0 - exit - ", - [0x22, 0x11], - 0x1122 -); - -test_cranelift!( - test_cranelift_le32, - " - ldxw r0, [r1] - le32 r0 - exit - ", - [0x44, 0x33, 0x22, 0x11], - 0x11223344 -); - -test_cranelift!( - test_cranelift_le64, - " - ldxdw r0, [r1] - le64 r0 - exit - ", - [0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11], - 0x1122334455667788 -); - -test_cranelift!( - test_cranelift_lsh_reg, - " - mov r0, 0x1 - mov r7, 4 - lsh r0, r7 - exit - ", - 0x10 -); - -test_cranelift!( - test_cranelift_mod, - " - mov32 r0, 5748 - mod32 r0, 92 - mov32 r1, 13 - mod32 r0, r1 - exit - ", - 0x5 -); - -test_cranelift!( - test_cranelift_mod32, - " - lddw r0, 0x100000003 - mod32 r0, 3 - exit - ", - 0x0 -); - -test_cranelift!( - test_cranelift_mod64, - " - mov32 r0, -1316649930 - lsh r0, 32 - or r0, 0x100dc5c8 - mov32 r1, 0xdde263e - lsh r1, 32 - or r1, 0x3cbef7f3 - mod r0, r1 - mod r0, 0x658f1778 - exit - ", - 0x30ba5a04 -); - -test_cranelift!( - test_cranelift_mov, - " - mov32 r1, 1 - mov32 r0, r1 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_mul32_imm, - " - mov r0, 3 - mul32 r0, 4 - exit - ", - 0xc -); - -test_cranelift!( - test_cranelift_mul32_reg, - " - mov r0, 3 - mov r1, 4 - mul32 r0, r1 - exit - ", - 0xc -); - -test_cranelift!( - test_cranelift_mul32_reg_overflow, - " - mov r0, 0x40000001 - mov r1, 4 - mul32 r0, r1 - exit - ", - 0x4 -); - -test_cranelift!( - test_cranelift_mul64_imm, - " - mov r0, 0x40000001 - mul r0, 4 - exit - ", - 0x100000004 -); - -test_cranelift!( - test_cranelift_mul64_reg, - " - mov r0, 0x40000001 - mov r1, 4 - mul r0, r1 - exit - ", - 0x100000004 -); - -test_cranelift!( - test_cranelift_mul_loop, - " - mov r0, 0x7 - add r1, 0xa - lsh r1, 0x20 - rsh r1, 0x20 - jeq r1, 0x0, +4 - mov r0, 0x7 - mul r0, 0x7 - add r1, -1 - jne r1, 0x0, -3 - exit - ", - 0x75db9c97 -); - -test_cranelift!( - test_cranelift_neg64, - " - mov32 r0, 2 - neg r0 - exit - ", - 0xfffffffffffffffe -); - -test_cranelift!( - test_cranelift_neg, - " - mov32 r0, 2 - neg32 r0 - exit - ", - 0xfffffffe -); - -test_cranelift!( - test_cranelift_prime, - " - mov r1, 67 - mov r0, 0x1 - mov r2, 0x2 - jgt r1, 0x2, +4 - ja +10 - add r2, 0x1 - mov r0, 0x1 - jge r2, r1, +7 - mov r3, r1 - div r3, r2 - mul r3, r2 - mov r4, r1 - sub r4, r3 - mov r0, 0x0 - jne r4, 0x0, -10 - exit - ", - 1 -); - -test_cranelift!( - test_cranelift_rhs32, - " - xor r0, r0 - sub r0, 1 - rsh32 r0, 8 - exit - ", - 0x00ffffff -); - -test_cranelift!( - test_cranelift_rsh_reg, - " - mov r0, 0x10 - mov r7, 4 - rsh r0, r7 - exit - ", - 0x1 -); - -test_cranelift!( - test_cranelift_stack, - " - mov r1, 51 - stdw [r10-16], 0xab - stdw [r10-8], 0xcd - and r1, 1 - lsh r1, 3 - mov r2, r10 - add r2, r1 - ldxdw r0, [r2-16] - exit - ", - 0xcd -); - -#[test] -fn test_cranelift_stack2() { - let prog = assemble( - " - stb [r10-4], 0x01 - stb [r10-3], 0x02 - stb [r10-2], 0x03 - stb [r10-1], 0x04 - mov r1, r10 - mov r2, 0x4 - sub r1, r2 - call 1 - mov r1, 0 - ldxb r2, [r10-4] - ldxb r3, [r10-3] - ldxb r4, [r10-2] - ldxb r5, [r10-1] - call 0 - xor r0, 0x2a2a2a2a - exit", - ) - .unwrap(); - - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.register_helper(0, helpers::gather_bytes).unwrap(); - vm.register_helper(1, helpers::memfrob).unwrap(); - vm.cranelift_compile().unwrap(); - assert_eq!(vm.execute_program_cranelift().unwrap(), 0x01020304); -} - -test_cranelift!( - test_cranelift_stb, - " - stb [r1+2], 0x11 - ldxb r0, [r1+2] - exit - ", - [0xaa, 0xbb, 0xff, 0xcc, 0xdd], - 0x11 -); - -test_cranelift!( - test_cranelift_stdw, - " - stdw [r1+2], 0x44332211 - ldxdw r0, [r1+2] - exit - ", - [0xaa, 0xbb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcc, 0xdd], - 0x44332211 -); - -test_cranelift!( - test_cranelift_sth, - " - sth [r1+2], 0x2211 - ldxh r0, [r1+2] - exit - ", - [0xaa, 0xbb, 0xff, 0xff, 0xcc, 0xdd], - 0x2211 -); - -#[test] -#[ignore] -fn test_cranelift_string_stack() { - let prog = assemble( - " - mov r1, 0x78636261 - stxw [r10-8], r1 - mov r6, 0x0 - stxb [r10-4], r6 - stxb [r10-12], r6 - mov r1, 0x79636261 - stxw [r10-16], r1 - mov r1, r10 - add r1, -8 - mov r2, r1 - call 0x4 - mov r1, r0 - mov r0, 0x1 - lsh r1, 0x20 - rsh r1, 0x20 - jne r1, 0x0, +11 - mov r1, r10 - add r1, -8 - mov r2, r10 - add r2, -16 - call 0x4 - mov r1, r0 - lsh r1, 0x20 - rsh r1, 0x20 - mov r0, 0x1 - jeq r1, r6, +1 - mov r0, 0x0 - exit", - ) - .unwrap(); - - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.register_helper(4, helpers::strcmp).unwrap(); - vm.cranelift_compile().unwrap(); - assert_eq!(vm.execute_program_cranelift().unwrap(), 0x0); -} - -test_cranelift!( - test_cranelift_stw, - " - stw [r1+2], 0x44332211 - ldxw r0, [r1+2] - exit - ", - [0xaa, 0xbb, 0xff, 0xff, 0xff, 0xff, 0xcc, 0xdd], - 0x44332211 -); - -test_cranelift!( - test_cranelift_stxb, - " - mov32 r2, 0x11 - stxb [r1+2], r2 - ldxb r0, [r1+2] - exit - ", - [0xaa, 0xbb, 0xff, 0xcc, 0xdd], - 0x11 -); - -test_cranelift!( - test_cranelift_stxb_all, - " - mov r0, 0xf0 - mov r2, 0xf2 - mov r3, 0xf3 - mov r4, 0xf4 - mov r5, 0xf5 - mov r6, 0xf6 - mov r7, 0xf7 - mov r8, 0xf8 - stxb [r1], r0 - stxb [r1+1], r2 - stxb [r1+2], r3 - stxb [r1+3], r4 - stxb [r1+4], r5 - stxb [r1+5], r6 - stxb [r1+6], r7 - stxb [r1+7], r8 - ldxdw r0, [r1] - be64 r0 - exit - ", - [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], - 0xf0f2f3f4f5f6f7f8 -); - -test_cranelift!( - test_cranelift_stxb_all2, - " - mov r0, r1 - mov r1, 0xf1 - mov r9, 0xf9 - stxb [r0], r1 - stxb [r0+1], r9 - ldxh r0, [r0] - be16 r0 - exit - ", - [0xff, 0xff], - 0xf1f9 -); - -test_cranelift!( - test_cranelift_stxb_chain, - " - mov r0, r1 - ldxb r9, [r0+0] - stxb [r0+1], r9 - ldxb r8, [r0+1] - stxb [r0+2], r8 - ldxb r7, [r0+2] - stxb [r0+3], r7 - ldxb r6, [r0+3] - stxb [r0+4], r6 - ldxb r5, [r0+4] - stxb [r0+5], r5 - ldxb r4, [r0+5] - stxb [r0+6], r4 - ldxb r3, [r0+6] - stxb [r0+7], r3 - ldxb r2, [r0+7] - stxb [r0+8], r2 - ldxb r1, [r0+8] - stxb [r0+9], r1 - ldxb r0, [r0+9] - exit - ", - [0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], - 0x2a -); - -test_cranelift!( - test_cranelift_stxdw, - " - mov r2, -2005440939 - lsh r2, 32 - or r2, 0x44332211 - stxdw [r1+2], r2 - ldxdw r0, [r1+2] - exit - ", - [0xaa, 0xbb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcc, 0xdd], - 0x8877665544332211 -); - -test_cranelift!( - test_cranelift_stxh, - " - mov32 r2, 0x2211 - stxh [r1+2], r2 - ldxh r0, [r1+2] - exit - ", - [0xaa, 0xbb, 0xff, 0xff, 0xcc, 0xdd], - 0x2211 -); - -test_cranelift!( - test_cranelift_stxw, - " - mov32 r2, 0x44332211 - stxw [r1+2], r2 - ldxw r0, [r1+2] - exit - ", - [0xaa, 0xbb, 0xff, 0xff, 0xff, 0xff, 0xcc, 0xdd], - 0x44332211 -); - -test_cranelift!( - test_cranelift_subnet, - " - mov r2, 0xe - ldxh r3, [r1+12] - jne r3, 0x81, +2 - mov r2, 0x12 - ldxh r3, [r1+16] - and r3, 0xffff - jne r3, 0x8, +5 - add r1, r2 - mov r0, 0x1 - ldxw r1, [r1+16] - and r1, 0xffffff - jeq r1, 0x1a8c0, +1 - mov r0, 0x0 - exit - ", - [ - 0x00, 0x00, 0xc0, 0x9f, 0xa0, 0x97, 0x00, 0xa0, 0xcc, 0x3b, 0xbf, 0xfa, 0x08, 0x00, 0x45, - 0x10, 0x00, 0x3c, 0x46, 0x3c, 0x40, 0x00, 0x40, 0x06, 0x73, 0x1c, 0xc0, 0xa8, 0x01, 0x02, - 0xc0, 0xa8, 0x01, 0x01, 0x06, 0x0e, 0x00, 0x17, 0x99, 0xc5, 0xa0, 0xec, 0x00, 0x00, 0x00, - 0x00, 0xa0, 0x02, 0x7d, 0x78, 0xe0, 0xa3, 0x00, 0x00, 0x02, 0x04, 0x05, 0xb4, 0x04, 0x02, - 0x08, 0x0a, 0x00, 0x9c, 0x27, 0x24, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x00, - ], - 0x1 -); - -const PROG_TCP_PORT_80: [u8; 152] = [ - 0x71, 0x12, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x71, 0x13, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x67, 0x03, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x4f, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x03, 0x0c, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x71, 0x12, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x02, 0x0a, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x71, 0x12, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x01, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, - 0x57, 0x02, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x67, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x0f, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0x12, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x15, 0x02, 0x02, 0x00, 0x00, 0x50, 0x00, 0x00, 0x69, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x55, 0x01, 0x01, 0x00, 0x00, 0x50, 0x00, 0x00, 0xb7, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -]; - -#[test] -fn test_cranelift_tcp_port80_match() { - let mem = &mut [ - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x00, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x08, 0x00, 0x45, - 0x00, 0x00, 0x56, 0x00, 0x01, 0x00, 0x00, 0x40, 0x06, 0xf9, 0x4d, 0xc0, 0xa8, 0x00, 0x01, - 0xc0, 0xa8, 0x00, 0x02, 0x27, 0x10, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x50, 0x02, 0x20, 0x00, 0xc5, 0x18, 0x00, 0x00, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - ]; - let prog = &PROG_TCP_PORT_80; - let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap(); - vm.cranelift_compile().unwrap(); - assert_eq!(vm.execute_program_cranelift(mem).unwrap(), 0x1); -} - -#[test] -fn test_cranelift_tcp_port80_nomatch() { - let mem = &mut [ - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x00, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x08, 0x00, 0x45, - 0x00, 0x00, 0x56, 0x00, 0x01, 0x00, 0x00, 0x40, 0x06, 0xf9, 0x4d, 0xc0, 0xa8, 0x00, 0x01, - 0xc0, 0xa8, 0x00, 0x02, 0x00, 0x16, 0x27, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x51, 0x02, 0x20, 0x00, 0xc5, 0x18, 0x00, 0x00, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - ]; - let prog = &PROG_TCP_PORT_80; - let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap(); - vm.cranelift_compile().unwrap(); - assert_eq!(vm.execute_program_cranelift(mem).unwrap(), 0x0); -} - -#[test] -fn test_cranelift_tcp_port80_nomatch_ethertype() { - let mem = &mut [ - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x00, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x08, 0x01, 0x45, - 0x00, 0x00, 0x56, 0x00, 0x01, 0x00, 0x00, 0x40, 0x06, 0xf9, 0x4d, 0xc0, 0xa8, 0x00, 0x01, - 0xc0, 0xa8, 0x00, 0x02, 0x27, 0x10, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x50, 0x02, 0x20, 0x00, 0xc5, 0x18, 0x00, 0x00, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - ]; - let prog = &PROG_TCP_PORT_80; - let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap(); - vm.cranelift_compile().unwrap(); - assert_eq!(vm.execute_program_cranelift(mem).unwrap(), 0x0); -} - -#[test] -fn test_cranelift_tcp_port80_nomatch_proto() { - let mem = &mut [ - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x00, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x08, 0x00, 0x45, - 0x00, 0x00, 0x56, 0x00, 0x01, 0x00, 0x00, 0x40, 0x11, 0xf9, 0x4d, 0xc0, 0xa8, 0x00, 0x01, - 0xc0, 0xa8, 0x00, 0x02, 0x27, 0x10, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x50, 0x02, 0x20, 0x00, 0xc5, 0x18, 0x00, 0x00, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - ]; - let prog = &PROG_TCP_PORT_80; - let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap(); - vm.cranelift_compile().unwrap(); - assert_eq!(vm.execute_program_cranelift(mem).unwrap(), 0x0); -} - -#[test] -fn test_cranelift_tcp_sack_match() { - let mut mem = TCP_SACK_MATCH.to_vec(); - let prog = assemble(TCP_SACK_ASM).unwrap(); - let mut vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - vm.cranelift_compile().unwrap(); - assert_eq!( - vm.execute_program_cranelift(mem.as_mut_slice()).unwrap(), - 0x1 - ); -} - -#[test] -fn test_cranelift_tcp_sack_nomatch() { - let mut mem = TCP_SACK_NOMATCH.to_vec(); - let prog = assemble(TCP_SACK_ASM).unwrap(); - let mut vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - vm.cranelift_compile().unwrap(); - assert_eq!( - vm.execute_program_cranelift(mem.as_mut_slice()).unwrap(), - 0x0 - ); -} - -#[test] -fn test_cranelift_ldabsb() { - let prog = &[ - 0x30, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, - ]; - let mem = &mut [ - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, - 0xff, - ]; - let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x00, 0x08).unwrap(); - - vm.cranelift_compile().unwrap(); - assert_eq!(vm.execute_program_cranelift(mem).unwrap(), 0x33); -} - -#[test] -fn test_cranelift_ldabsh() { - let prog = &[ - 0x28, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, - ]; - let mem = &mut [ - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, - 0xff, - ]; - let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x00, 0x08).unwrap(); - - vm.cranelift_compile().unwrap(); - assert_eq!(vm.execute_program_cranelift(mem).unwrap(), 0x4433); -} - -#[test] -fn test_cranelift_ldabsw() { - let prog = &[ - 0x20, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, - ]; - let mem = &mut [ - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, - 0xff, - ]; - let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x00, 0x08).unwrap(); - - vm.cranelift_compile().unwrap(); - assert_eq!(vm.execute_program_cranelift(mem).unwrap(), 0x66554433); -} - -#[test] -fn test_cranelift_ldabsdw() { - let prog = &[ - 0x38, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, - ]; - let mem = &mut [ - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, - 0xff, - ]; - let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x00, 0x08).unwrap(); - - vm.cranelift_compile().unwrap(); - assert_eq!( - vm.execute_program_cranelift(mem).unwrap(), - 0xaa99887766554433 - ); -} - -#[test] -fn test_cranelift_ldindb() { - let prog = &[ - 0xb7, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x50, 0x10, 0x00, 0x00, 0x03, 0x00, 0x00, - 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ]; - let mem = &mut [ - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, - 0xff, - ]; - let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x00, 0x08).unwrap(); - - vm.cranelift_compile().unwrap(); - assert_eq!(vm.execute_program_cranelift(mem).unwrap(), 0x88); -} - -#[test] -fn test_cranelift_ldindh() { - let prog = &[ - 0xb7, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x48, 0x10, 0x00, 0x00, 0x03, 0x00, 0x00, - 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ]; - let mem = &mut [ - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, - 0xff, - ]; - let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x00, 0x08).unwrap(); - - vm.cranelift_compile().unwrap(); - assert_eq!(vm.execute_program_cranelift(mem).unwrap(), 0x9988); -} - -#[test] -fn test_cranelift_ldindw() { - let prog = &[ - 0xb7, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x40, 0x10, 0x00, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ]; - let mem = &mut [ - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, - 0xff, - ]; - let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x00, 0x08).unwrap(); - - vm.cranelift_compile().unwrap(); - assert_eq!(vm.execute_program_cranelift(mem).unwrap(), 0x88776655); -} - -#[test] -fn test_cranelift_ldinddw() { - let prog = &[ - 0xb7, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x58, 0x10, 0x00, 0x00, 0x03, 0x00, 0x00, - 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ]; - let mem = &mut [ - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, - 0xff, - ]; - let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x00, 0x08).unwrap(); - - vm.cranelift_compile().unwrap(); - assert_eq!( - vm.execute_program_cranelift(mem).unwrap(), - 0xccbbaa9988776655 - ); -} diff --git a/kernel/crates/rbpf/tests/disassembler.rs b/kernel/crates/rbpf/tests/disassembler.rs deleted file mode 100644 index 90f7ebe4..00000000 --- a/kernel/crates/rbpf/tests/disassembler.rs +++ /dev/null @@ -1,377 +0,0 @@ -// SPDX-License-Identifier: (Apache-2.0 OR MIT) -// Copyright 2017 Jan-Erik Rediger -// -// Adopted from tests in `tests/assembler.rs` - -extern crate rbpf; -mod common; - -use rbpf::{assembler::assemble, disassembler::to_insn_vec}; - -// Using a macro to keep actual line numbers in failure output -macro_rules! disasm { - ($src:expr) => {{ - let src = $src; - let asm = assemble(src).expect("Can't assemble from string"); - let insn = to_insn_vec(&asm); - let reasm = insn - .into_iter() - .map(|ins| ins.desc) - .collect::>() - .join("\n"); - - assert_eq!(src, reasm); - }}; -} - -#[test] -fn test_empty() { - disasm!(""); -} - -// Example for InstructionType::NoOperand. -#[test] -fn test_exit() { - disasm!("exit"); -} - -// Example for InstructionType::AluBinary. -#[test] -fn test_add64() { - disasm!("add64 r1, r3"); - disasm!("add64 r1, 0x5"); -} - -// Example for InstructionType::AluUnary. -#[test] -fn test_neg64() { - disasm!("neg64 r1"); -} - -// Example for InstructionType::LoadReg. -#[test] -fn test_ldxw() { - disasm!("ldxw r1, [r2+0x5]"); -} - -// Example for InstructionType::StoreImm. -#[test] -fn test_stw() { - disasm!("stw [r2+0x5], 0x7"); -} - -// Example for InstructionType::StoreReg. -#[test] -fn test_stxw() { - disasm!("stxw [r2+0x5], r8"); -} - -// Example for InstructionType::JumpUnconditional. -#[test] -fn test_ja() { - disasm!("ja +0x8"); -} - -// Example for InstructionType::JumpConditional. -#[test] -fn test_jeq() { - disasm!("jeq r1, 0x4, +0x8"); - disasm!("jeq r1, r3, +0x8"); -} - -// Example for InstructionType::Call. -#[test] -fn test_call() { - disasm!("call 0x3"); -} - -// Example for InstructionType::Endian. -#[test] -fn test_be32() { - disasm!("be32 r1"); -} - -// Example for InstructionType::LoadImm. -#[test] -fn test_lddw() { - disasm!("lddw r1, 0x1234abcd5678eeff"); - disasm!("lddw r1, 0xff11ee22dd33cc44"); -} - -// Example for InstructionType::LoadAbs. -#[test] -fn test_ldabsw() { - disasm!("ldabsw 0x1"); -} - -// Example for InstructionType::LoadInd. -#[test] -fn test_ldindw() { - disasm!("ldindw r1, 0x2"); -} - -// Example for InstructionType::LoadReg. -#[test] -fn test_ldxdw() { - disasm!("ldxdw r1, [r2+0x3]"); -} - -// Example for InstructionType::StoreImm. -#[test] -fn test_sth() { - disasm!("sth [r1+0x2], 0x3"); -} - -// Example for InstructionType::StoreReg. -#[test] -fn test_stxh() { - disasm!("stxh [r1+0x2], r3"); -} - -// Test all supported AluBinary mnemonics. -#[test] -fn test_alu_binary() { - disasm!( - "add64 r1, r2 -sub64 r1, r2 -mul64 r1, r2 -div64 r1, r2 -or64 r1, r2 -and64 r1, r2 -lsh64 r1, r2 -rsh64 r1, r2 -mod64 r1, r2 -xor64 r1, r2 -mov64 r1, r2 -arsh64 r1, r2" - ); - - disasm!( - "add64 r1, 0x2 -sub64 r1, 0x2 -mul64 r1, 0x2 -div64 r1, 0x2 -or64 r1, 0x2 -and64 r1, 0x2 -lsh64 r1, 0x2 -rsh64 r1, 0x2 -mod64 r1, 0x2 -xor64 r1, 0x2 -mov64 r1, 0x2 -arsh64 r1, 0x2" - ); - - disasm!( - "add32 r1, r2 -sub32 r1, r2 -mul32 r1, r2 -div32 r1, r2 -or32 r1, r2 -and32 r1, r2 -lsh32 r1, r2 -rsh32 r1, r2 -mod32 r1, r2 -xor32 r1, r2 -mov32 r1, r2 -arsh32 r1, r2" - ); - - disasm!( - "add32 r1, 0x2 -sub32 r1, 0x2 -mul32 r1, 0x2 -div32 r1, 0x2 -or32 r1, 0x2 -and32 r1, 0x2 -lsh32 r1, 0x2 -rsh32 r1, 0x2 -mod32 r1, 0x2 -xor32 r1, 0x2 -mov32 r1, 0x2 -arsh32 r1, 0x2" - ); -} - -// Test all supported AluUnary mnemonics. -#[test] -fn test_alu_unary() { - disasm!( - "neg64 r1 -neg32 r1" - ); -} - -// Test all supported LoadAbs mnemonics. -#[test] -fn test_load_abs() { - disasm!( - "ldabsw 0x1 -ldabsh 0x1 -ldabsb 0x1 -ldabsdw 0x1" - ); -} - -// Test all supported LoadInd mnemonics. -#[test] -fn test_load_ind() { - disasm!( - "ldindw r1, 0x2 -ldindh r1, 0x2 -ldindb r1, 0x2 -ldinddw r1, 0x2" - ); -} - -// Test all supported LoadReg mnemonics. -#[test] -fn test_load_reg() { - disasm!( - r"ldxw r1, [r2+0x3] -ldxh r1, [r2+0x3] -ldxb r1, [r2+0x3] -ldxdw r1, [r2+0x3]" - ); -} - -// Test all supported StoreImm mnemonics. -#[test] -fn test_store_imm() { - disasm!( - "stw [r1+0x2], 0x3 -sth [r1+0x2], 0x3 -stb [r1+0x2], 0x3 -stdw [r1+0x2], 0x3" - ); -} - -// Test all supported StoreReg mnemonics. -#[test] -fn test_store_reg() { - disasm!( - "stxw [r1+0x2], r3 -stxh [r1+0x2], r3 -stxb [r1+0x2], r3 -stxdw [r1+0x2], r3" - ); -} - -// Test all supported JumpConditional mnemonics. -#[test] -fn test_jump_conditional() { - disasm!( - "jeq r1, r2, +0x3 -jgt r1, r2, +0x3 -jge r1, r2, +0x3 -jlt r1, r2, +0x3 -jle r1, r2, +0x3 -jset r1, r2, +0x3 -jne r1, r2, +0x3 -jsgt r1, r2, +0x3 -jsge r1, r2, -0x3 -jslt r1, r2, +0x3 -jsle r1, r2, -0x3" - ); - - disasm!( - "jeq r1, 0x2, +0x3 -jgt r1, 0x2, +0x3 -jge r1, 0x2, +0x3 -jlt r1, 0x2, +0x3 -jle r1, 0x2, +0x3 -jset r1, 0x2, +0x3 -jne r1, 0x2, +0x3 -jsgt r1, 0x2, +0x3 -jsge r1, 0x2, -0x3 -jslt r1, 0x2, +0x3 -jsle r1, 0x2, -0x3" - ); - - disasm!( - "jeq32 r1, r2, +0x3 -jgt32 r1, r2, +0x3 -jge32 r1, r2, +0x3 -jlt32 r1, r2, +0x3 -jle32 r1, r2, +0x3 -jset32 r1, r2, +0x3 -jne32 r1, r2, +0x3 -jsgt32 r1, r2, +0x3 -jsge32 r1, r2, -0x3 -jslt32 r1, r2, +0x3 -jsle32 r1, r2, -0x3" - ); - - disasm!( - "jeq32 r1, 0x2, +0x3 -jgt32 r1, 0x2, +0x3 -jge32 r1, 0x2, +0x3 -jlt32 r1, 0x2, +0x3 -jle32 r1, 0x2, +0x3 -jset32 r1, 0x2, +0x3 -jne32 r1, 0x2, +0x3 -jsgt32 r1, 0x2, +0x3 -jsge32 r1, 0x2, -0x3 -jslt32 r1, 0x2, +0x3 -jsle32 r1, 0x2, -0x3" - ); -} - -// Test all supported Endian mnemonics. -#[test] -fn test_endian() { - disasm!( - "be16 r1 -be32 r1 -be64 r1 -le16 r1 -le32 r1 -le64 r1" - ); -} - -#[test] -fn test_large_immediate() { - disasm!("add64 r1, 0x7fffffff"); - disasm!("add64 r1, 0x7fffffff"); -} - -// Non-regression tests for overflow when trying to negate offset 0x8000i16. -#[test] -fn test_offset_overflow() { - let insns = [ - 0x62, 0x01, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, // stw - 0x6a, 0x01, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, // sth - 0x72, 0x01, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, // stb - 0x7a, 0x01, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, // stdw - 0x61, 0x01, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, // ldxw - 0x69, 0x01, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, // ldxh - 0x71, 0x01, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, // ldxb - 0x79, 0x01, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, // ldxdw - 0x15, 0x01, 0x00, 0x80, 0x02, 0x00, 0x00, 0x00, // jeq (imm) - 0x1d, 0x21, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, // jeq (reg) - 0x16, 0x01, 0x00, 0x80, 0x02, 0x00, 0x00, 0x00, // jeq32 (imm) - 0x1e, 0x21, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, // jeq32 (reg) - ]; - - let expected_output = "stw [r1-0x8000], 0x1 -sth [r1-0x8000], 0x1 -stb [r1-0x8000], 0x1 -stdw [r1-0x8000], 0x1 -ldxw r1, [r0-0x8000] -ldxh r1, [r0-0x8000] -ldxb r1, [r0-0x8000] -ldxdw r1, [r0-0x8000] -jeq r1, 0x2, -0x8000 -jeq r1, r2, -0x8000 -jeq32 r1, 0x2, -0x8000 -jeq32 r1, r2, -0x8000"; - - let prog = to_insn_vec(&insns); - let asm = prog - .into_iter() - .map(|ins| ins.desc) - .collect::>() - .join("\n"); - - assert_eq!(asm, expected_output); -} diff --git a/kernel/crates/rbpf/tests/misc.rs b/kernel/crates/rbpf/tests/misc.rs deleted file mode 100644 index 10bb053d..00000000 --- a/kernel/crates/rbpf/tests/misc.rs +++ /dev/null @@ -1,571 +0,0 @@ -// SPDX-License-Identifier: (Apache-2.0 OR MIT) -// Copyright 2016 6WIND S.A. - -// There are unused mut warnings due to unsafe code. -#![allow(unused_mut)] -#![allow(clippy::unreadable_literal)] - -// This crate would be needed to load bytecode from a BPF-compiled object file. Since the crate -// is not used anywhere else in the library, it is deactivated: we do not want to load and compile -// it just for the tests. If you want to use it, do not forget to add the following -// dependency to your Cargo.toml file: -// -// --- -// elf = "0.0.10" -// --- -// -// extern crate elf; -// use std::path::PathBuf; - -extern crate rbpf; - -#[cfg(feature = "std")] -use rbpf::helpers; -use rbpf::{assembler::assemble, Error, ErrorKind}; - -// The following two examples have been compiled from C with the following command: -// -// ```bash -// clang -O2 -emit-llvm -c -o - | llc -march=bpf -filetype=obj -o -// ``` -// -// The C source code was the following: -// -// ```c -// #include -// #include -// #include -// #include -// -// #define ETH_ALEN 6 -// #define ETH_P_IP 0x0008 /* htons(0x0800) */ -// #define TCP_HDR_LEN 20 -// -// #define BLOCKED_TCP_PORT 0x9999 -// -// struct eth_hdr { -// unsigned char h_dest[ETH_ALEN]; -// unsigned char h_source[ETH_ALEN]; -// unsigned short h_proto; -// }; -// -// #define SEC(NAME) __attribute__((section(NAME), used)) -// SEC(".classifier") -// int handle_ingress(struct __sk_buff *skb) -// { -// void *data = (void *)(long)skb->data; -// void *data_end = (void *)(long)skb->data_end; -// struct eth_hdr *eth = data; -// struct iphdr *iph = data + sizeof(*eth); -// struct tcphdr *tcp = data + sizeof(*eth) + sizeof(*iph); -// -// /* single length check */ -// if (data + sizeof(*eth) + sizeof(*iph) + sizeof(*tcp) > data_end) -// return 0; -// if (eth->h_proto != ETH_P_IP) -// return 0; -// if (iph->protocol != IPPROTO_TCP) -// return 0; -// if (tcp->source == BLOCKED_TCP_PORT || tcp->dest == BLOCKED_TCP_PORT) -// return -1; -// return 0; -// } -// char _license[] SEC(".license") = "GPL"; -// ``` -// -// This program, once compiled, can be injected into Linux kernel, with tc for instance. Sadly, we -// need to bring some modifications to the generated bytecode in order to run it: the three -// instructions with opcode 0x61 load data from a packet area as 4-byte words, where we need to -// load it as 8-bytes double words (0x79). The kernel does the same kind of translation before -// running the program, but rbpf does not implement this. -// -// In addition, the offset at which the pointer to the packet data is stored must be changed: since -// we use 8 bytes instead of 4 for the start and end addresses of the data packet, we cannot use -// the offsets produced by clang (0x4c and 0x50), the addresses would overlap. Instead we can use, -// for example, 0x40 and 0x50. See comments on the bytecode below to see the modifications. -// -// Once the bytecode has been (manually, in our case) edited, we can load the bytecode directly -// from the ELF object file. This is easy to do, but requires the addition of two crates in the -// Cargo.toml file (see comments above), so here we use just the hardcoded bytecode instructions -// instead. - -#[test] -#[cfg(feature = "std")] -fn test_vm_block_port() { - // To load the bytecode from an object file instead of using the hardcoded instructions, - // use the additional crates commented at the beginning of this file (and also add them to your - // Cargo.toml). See comments above. - // - // --- - // let filename = "my_ebpf_object_file.o"; - // - // let path = PathBuf::from(filename); - // let file = match elf::File::open_path(&path) { - // Ok(f) => f, - // Err(e) => panic!("Error: {:?}", e), - // }; - // - // let text_scn = match file.get_section(".classifier") { - // Some(s) => s, - // None => panic!("Failed to look up .classifier section"), - // }; - // - // let prog = &text_scn.data; - // --- - - let prog = &[ - 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x12, 0x50, 0x00, 0x00, 0x00, 0x00, - 0x00, // 0x79 instead of 0x61 - 0x79, 0x11, 0x40, 0x00, 0x00, 0x00, 0x00, - 0x00, // 0x79 instead of 0x61, 0x40 i.o. 0x4c - 0xbf, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x03, 0x00, 0x00, 0x36, 0x00, 0x00, - 0x00, 0x2d, 0x23, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0x12, 0x0c, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x55, 0x02, 0x10, 0x00, 0x08, 0x00, 0x00, 0x00, 0x71, 0x12, 0x17, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x55, 0x02, 0x0e, 0x00, 0x06, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x11, 0x22, - 0x00, 0x00, 0x00, 0x00, 0x00, // 0x79 instead of 0x61 - 0xbf, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x02, 0x00, 0x00, 0xff, 0xff, 0x00, - 0x00, 0x15, 0x02, 0x08, 0x00, 0x99, 0x99, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x21, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xb7, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x18, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x99, 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x21, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ]; - - let packet = &mut [ - 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x08, - 0x00, // ethertype - 0x45, 0x00, 0x00, 0x3b, // start ip_hdr - 0xa6, 0xab, 0x40, 0x00, 0x40, 0x06, 0x96, 0x0f, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00, 0x00, - 0x01, - // Program matches the next two bytes: 0x9999 returns 0xffffffff, else return 0. - 0x99, 0x99, 0xc6, 0xcc, // start tcp_hdr - 0xd1, 0xe5, 0xc4, 0x9d, 0xd4, 0x30, 0xb5, 0xd2, 0x80, 0x18, 0x01, 0x56, 0xfe, 0x2f, 0x00, - 0x00, 0x01, 0x01, 0x08, 0x0a, // start data - 0x00, 0x23, 0x75, 0x89, 0x00, 0x23, 0x63, 0x2d, 0x71, 0x64, 0x66, 0x73, 0x64, 0x66, 0x0au8, - ]; - - let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap(); - vm.register_helper(helpers::BPF_TRACE_PRINTK_IDX, helpers::bpf_trace_printf) - .unwrap(); - - let res = vm.execute_program(packet).unwrap(); - println!("Program returned: {res:?} ({res:#x})"); - assert_eq!(res, 0xffffffff); -} - -#[test] -#[cfg(all(not(windows), feature = "std"))] -fn test_jit_block_port() { - // To load the bytecode from an object file instead of using the hardcoded instructions, - // use the additional crates commented at the beginning of this file (and also add them to your - // Cargo.toml). See comments above. - // - // --- - // let filename = "my_ebpf_object_file.o"; - // - // let path = PathBuf::from(filename); - // let file = match elf::File::open_path(&path) { - // Ok(f) => f, - // Err(e) => panic!("Error: {:?}", e), - // }; - // - // let text_scn = match file.get_section(".classifier") { - // Some(s) => s, - // None => panic!("Failed to look up .classifier section"), - // }; - // - // let prog = &text_scn.data; - // --- - - let prog = &[ - 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x12, 0x50, 0x00, 0x00, 0x00, 0x00, - 0x00, // 0x79 instead of 0x61 - 0x79, 0x11, 0x40, 0x00, 0x00, 0x00, 0x00, - 0x00, // 0x79 instead of 0x61, 0x40 i.o. 0x4c - 0xbf, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x03, 0x00, 0x00, 0x36, 0x00, 0x00, - 0x00, 0x2d, 0x23, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0x12, 0x0c, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x55, 0x02, 0x10, 0x00, 0x08, 0x00, 0x00, 0x00, 0x71, 0x12, 0x17, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x55, 0x02, 0x0e, 0x00, 0x06, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, - 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x11, 0x22, - 0x00, 0x00, 0x00, 0x00, 0x00, // 0x79 instead of 0x61 - 0xbf, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x02, 0x00, 0x00, 0xff, 0xff, 0x00, - 0x00, 0x15, 0x02, 0x08, 0x00, 0x99, 0x99, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00, 0x00, 0x00, - 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x21, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xb7, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x18, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x99, 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x21, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ]; - - let packet = &mut [ - 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x08, - 0x00, // ethertype - 0x45, 0x00, 0x00, 0x3b, // start ip_hdr - 0xa6, 0xab, 0x40, 0x00, 0x40, 0x06, 0x96, 0x0f, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00, 0x00, - 0x01, - // Program matches the next two bytes: 0x9999 returns 0xffffffff, else return 0. - 0x99, 0x99, 0xc6, 0xcc, // start tcp_hdr - 0xd1, 0xe5, 0xc4, 0x9d, 0xd4, 0x30, 0xb5, 0xd2, 0x80, 0x18, 0x01, 0x56, 0xfe, 0x2f, 0x00, - 0x00, 0x01, 0x01, 0x08, 0x0a, // start data - 0x00, 0x23, 0x75, 0x89, 0x00, 0x23, 0x63, 0x2d, 0x71, 0x64, 0x66, 0x73, 0x64, 0x66, 0x0au8, - ]; - - let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(prog), 0x40, 0x50).unwrap(); - vm.register_helper(helpers::BPF_TRACE_PRINTK_IDX, helpers::bpf_trace_printf) - .unwrap(); - vm.jit_compile().unwrap(); - - unsafe { - let res = vm.execute_program_jit(packet).unwrap(); - println!("Program returned: {res:?} ({res:#x})"); - assert_eq!(res, 0xffffffff); - } -} - -// Program and memory come from uBPF test ldxh. -#[test] -fn test_vm_mbuff() { - let prog = &[ - // Load mem from mbuff into R1 - 0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // ldhx r1[2], r0 - 0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, - ]; - let mem = &[0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd]; - - let mbuff = [0u8; 32]; - unsafe { - let mut data = mbuff.as_ptr().offset(8) as *mut u64; - let mut data_end = mbuff.as_ptr().offset(24) as *mut u64; - data.write_unaligned(mem.as_ptr() as u64); - data_end.write_unaligned(mem.as_ptr() as u64 + mem.len() as u64); - } - - let vm = rbpf::EbpfVmMbuff::new(Some(prog)).unwrap(); - assert_eq!(vm.execute_program(mem, &mbuff).unwrap(), 0x2211); -} - -// Program and memory come from uBPF test ldxh. -#[test] -fn test_vm_mbuff_with_rust_api() { - use rbpf::insn_builder::*; - - let mut program = BpfCode::new(); - program - .load_x(MemSize::DoubleWord) - .set_dst(0x01) - .set_src(0x01) - .set_off(0x00_08) - .push() - .load_x(MemSize::HalfWord) - .set_dst(0x00) - .set_src(0x01) - .set_off(0x00_02) - .push() - .exit() - .push(); - - let mem = &[0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd]; - - let mbuff = [0u8; 32]; - unsafe { - let mut data = mbuff.as_ptr().offset(8) as *mut u64; - let mut data_end = mbuff.as_ptr().offset(24) as *mut u64; - *data = mem.as_ptr() as u64; - *data_end = mem.as_ptr() as u64 + mem.len() as u64; - } - - let vm = rbpf::EbpfVmMbuff::new(Some(program.into_bytes())).unwrap(); - assert_eq!(vm.execute_program(mem, &mbuff).unwrap(), 0x2211); -} - -// Program and memory come from uBPF test ldxh. -#[test] -#[cfg(all(not(windows), feature = "std"))] -fn test_jit_mbuff() { - let prog = &[ - // Load mem from mbuff into R1 - 0x79, 0x11, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // ldhx r1[2], r0 - 0x69, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, - ]; - let mem = &mut [0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd]; - - let mut mbuff = [0u8; 32]; - unsafe { - let mut data = mbuff.as_ptr().offset(8) as *mut u64; - let mut data_end = mbuff.as_ptr().offset(24) as *mut u64; - *data = mem.as_ptr() as u64; - *data_end = mem.as_ptr() as u64 + mem.len() as u64; - } - - unsafe { - let mut vm = rbpf::EbpfVmMbuff::new(Some(prog)).unwrap(); - vm.jit_compile().unwrap(); - assert_eq!(vm.execute_program_jit(mem, &mut mbuff).unwrap(), 0x2211); - } -} - -#[cfg(all(not(windows), feature = "std"))] -#[test] -fn test_vm_jit_ldabsb() { - let prog = &[ - 0x30, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, - ]; - let mem = &mut [ - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, - 0xff, - ]; - let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap(); - assert_eq!(vm.execute_program(mem).unwrap(), 0x33); - - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x33); - }; -} - -#[cfg(all(not(windows), feature = "std"))] -#[test] -fn test_vm_jit_ldabsh() { - let prog = &[ - 0x28, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, - ]; - let mem = &mut [ - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, - 0xff, - ]; - let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap(); - assert_eq!(vm.execute_program(mem).unwrap(), 0x4433); - - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x4433); - }; -} - -#[cfg(all(not(windows), feature = "std"))] -#[test] -fn test_vm_jit_ldabsw() { - let prog = &[ - 0x20, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, - ]; - let mem = &mut [ - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, - 0xff, - ]; - let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap(); - assert_eq!(vm.execute_program(mem).unwrap(), 0x66554433); - vm.jit_compile().unwrap(); - - unsafe { - assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x66554433); - }; -} - -#[cfg(all(not(windows), feature = "std"))] -#[test] -fn test_vm_jit_ldabsdw() { - let prog = &[ - 0x38, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, - ]; - let mem = &mut [ - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, - 0xff, - ]; - let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap(); - assert_eq!(vm.execute_program(mem).unwrap(), 0xaa99887766554433); - vm.jit_compile().unwrap(); - - unsafe { - assert_eq!(vm.execute_program_jit(mem).unwrap(), 0xaa99887766554433); - }; -} - -#[test] -#[should_panic(expected = "Error: out of bounds memory load (insn #1),")] -fn test_vm_err_ldabsb_oob() { - let prog = &[ - 0x38, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, - ]; - let mem = &mut [ - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, - 0xff, - ]; - let vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap(); - vm.execute_program(mem).unwrap(); - - // Memory check not implemented for JIT yet. -} - -#[test] -#[should_panic(expected = "Error: out of bounds memory load (insn #1),")] -fn test_vm_err_ldabsb_nomem() { - let prog = &[ - 0x38, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, - ]; - let vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap(); - vm.execute_program().unwrap(); - - // Memory check not implemented for JIT yet. -} - -#[cfg(all(not(windows), feature = "std"))] -#[test] -fn test_vm_jit_ldindb() { - let prog = &[ - 0xb7, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x50, 0x10, 0x00, 0x00, 0x03, 0x00, 0x00, - 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ]; - let mem = &mut [ - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, - 0xff, - ]; - let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap(); - assert_eq!(vm.execute_program(mem).unwrap(), 0x88); - - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x88); - }; -} - -#[cfg(all(not(windows), feature = "std"))] -#[test] -fn test_vm_jit_ldindh() { - let prog = &[ - 0xb7, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x48, 0x10, 0x00, 0x00, 0x03, 0x00, 0x00, - 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ]; - let mem = &mut [ - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, - 0xff, - ]; - let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap(); - assert_eq!(vm.execute_program(mem).unwrap(), 0x9988); - - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x9988); - }; -} - -#[cfg(all(not(windows), feature = "std"))] -#[test] -fn test_vm_jit_ldindw() { - let prog = &[ - 0xb7, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x40, 0x10, 0x00, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ]; - let mem = &mut [ - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, - 0xff, - ]; - let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap(); - assert_eq!(vm.execute_program(mem).unwrap(), 0x88776655); - vm.jit_compile().unwrap(); - - unsafe { - assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x88776655); - }; -} - -#[cfg(all(not(windows), feature = "std"))] -#[test] -fn test_vm_jit_ldinddw() { - let prog = &[ - 0xb7, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x58, 0x10, 0x00, 0x00, 0x03, 0x00, 0x00, - 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ]; - let mem = &mut [ - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, - 0xff, - ]; - let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap(); - assert_eq!(vm.execute_program(mem).unwrap(), 0xccbbaa9988776655); - vm.jit_compile().unwrap(); - - unsafe { - assert_eq!(vm.execute_program_jit(mem).unwrap(), 0xccbbaa9988776655); - }; -} - -#[test] -#[should_panic(expected = "Error: out of bounds memory load (insn #2),")] -fn test_vm_err_ldindb_oob() { - let prog = &[ - 0xb7, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x38, 0x10, 0x00, 0x00, 0x33, 0x00, 0x00, - 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ]; - let mem = &mut [ - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, - 0xff, - ]; - let vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap(); - vm.execute_program(mem).unwrap(); - - // Memory check not implemented for JIT yet. -} - -#[test] -#[should_panic(expected = "Error: out of bounds memory load (insn #2),")] -fn test_vm_err_ldindb_nomem() { - let prog = &[ - 0xb7, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x38, 0x10, 0x00, 0x00, 0x03, 0x00, 0x00, - 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ]; - let vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap(); - vm.execute_program().unwrap(); - - // Memory check not implemented for JIT yet. -} - -#[test] -#[should_panic(expected = "Error: No program set, call prog_set() to load one")] -fn test_vm_exec_no_program() { - let vm = rbpf::EbpfVmNoData::new(None).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0xBEE); -} - -fn verifier_success(_prog: &[u8]) -> Result<(), Error> { - Ok(()) -} - -fn verifier_fail(_prog: &[u8]) -> Result<(), Error> { - Err(Error::new(ErrorKind::Other, "Gaggablaghblagh!")) -} - -#[test] -fn test_verifier_success() { - let prog = assemble( - "mov32 r0, 0xBEE - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(None).unwrap(); - vm.set_verifier(verifier_success).unwrap(); - vm.set_program(&prog).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0xBEE); -} - -#[test] -#[should_panic(expected = "Gaggablaghblagh!")] -fn test_verifier_fail() { - let prog = assemble( - "mov32 r0, 0xBEE - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(None).unwrap(); - vm.set_verifier(verifier_fail).unwrap(); - vm.set_program(&prog).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0xBEE); -} diff --git a/kernel/crates/rbpf/tests/ubpf_jit_x86_64.rs b/kernel/crates/rbpf/tests/ubpf_jit_x86_64.rs deleted file mode 100644 index 2740a0ae..00000000 --- a/kernel/crates/rbpf/tests/ubpf_jit_x86_64.rs +++ /dev/null @@ -1,2891 +0,0 @@ -// SPDX-License-Identifier: (Apache-2.0 OR MIT) -// Converted from the tests for uBPF -// Copyright 2015 Big Switch Networks, Inc -// Copyright 2016 6WIND S.A. - -// The tests contained in this file are extracted from the unit tests of uBPF software. Each test -// in this file has a name in the form `test_jit_`, and corresponds to the (human-readable) -// code in `ubpf/tree/master/tests/`, available at -// (hyphen had to be replaced with underscores -// as Rust will not accept them in function names). It is strongly advised to refer to the uBPF -// version to understand what these program do. -// -// Each program was assembled from the uBPF version with the assembler provided by uBPF itself, and -// available at . -// The very few modifications that have been realized should be indicated. - -// These are unit tests for the eBPF JIT compiler. - -#![allow(clippy::unreadable_literal)] -#![cfg(all(not(windows), feature = "std"))] - -extern crate rbpf; -mod common; - -use common::{TCP_SACK_ASM, TCP_SACK_MATCH, TCP_SACK_NOMATCH}; -use rbpf::{assembler::assemble, helpers}; - -#[test] -fn test_jit_add() { - let prog = assemble( - " - mov32 r0, 0 - mov32 r1, 2 - add32 r0, 1 - add32 r0, r1 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x3); - } -} - -#[test] -fn test_jit_alu64_arith() { - let prog = assemble( - " - mov r0, 0 - mov r1, 1 - mov r2, 2 - mov r3, 3 - mov r4, 4 - mov r5, 5 - mov r6, 6 - mov r7, 7 - mov r8, 8 - mov r9, 9 - add r0, 23 - add r0, r7 - sub r0, 13 - sub r0, r1 - mul r0, 7 - mul r0, r3 - div r0, 2 - div r0, r4 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x2a); - } -} - -#[test] -fn test_jit_alu64_bit() { - let prog = assemble( - " - mov r0, 0 - mov r1, 1 - mov r2, 2 - mov r3, 3 - mov r4, 4 - mov r5, 5 - mov r6, 6 - mov r7, 7 - mov r8, 8 - or r0, r5 - or r0, 0xa0 - and r0, 0xa3 - mov r9, 0x91 - and r0, r9 - lsh r0, 32 - lsh r0, 22 - lsh r0, r8 - rsh r0, 32 - rsh r0, 19 - rsh r0, r7 - xor r0, 0x03 - xor r0, r2 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x11); - } -} - -#[test] -fn test_jit_alu_arith() { - let prog = assemble( - " - mov32 r0, 0 - mov32 r1, 1 - mov32 r2, 2 - mov32 r3, 3 - mov32 r4, 4 - mov32 r5, 5 - mov32 r6, 6 - mov32 r7, 7 - mov32 r8, 8 - mov32 r9, 9 - add32 r0, 23 - add32 r0, r7 - sub32 r0, 13 - sub32 r0, r1 - mul32 r0, 7 - mul32 r0, r3 - div32 r0, 2 - div32 r0, r4 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x2a); - } -} - -#[test] -fn test_jit_alu_bit() { - let prog = assemble( - " - mov32 r0, 0 - mov32 r1, 1 - mov32 r2, 2 - mov32 r3, 3 - mov32 r4, 4 - mov32 r5, 5 - mov32 r6, 6 - mov32 r7, 7 - mov32 r8, 8 - or32 r0, r5 - or32 r0, 0xa0 - and32 r0, 0xa3 - mov32 r9, 0x91 - and32 r0, r9 - lsh32 r0, 22 - lsh32 r0, r8 - rsh32 r0, 19 - rsh32 r0, r7 - xor32 r0, 0x03 - xor32 r0, r2 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x11); - } -} - -#[test] -fn test_jit_arsh32_high_shift() { - let prog = assemble( - " - mov r0, 8 - lddw r1, 0x100000001 - arsh32 r0, r1 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x4); - } -} - -#[test] -fn test_jit_arsh() { - let prog = assemble( - " - mov32 r0, 0xf8 - lsh32 r0, 28 - arsh32 r0, 16 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0xffff8000); - } -} - -#[test] -fn test_jit_arsh64() { - let prog = assemble( - " - mov32 r0, 1 - lsh r0, 63 - arsh r0, 55 - mov32 r1, 5 - arsh r0, r1 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0xfffffffffffffff8); - } -} - -#[test] -fn test_jit_arsh_reg() { - let prog = assemble( - " - mov32 r0, 0xf8 - mov32 r1, 16 - lsh32 r0, 28 - arsh32 r0, r1 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0xffff8000); - } -} - -#[test] -fn test_jit_be16() { - let prog = assemble( - " - ldxh r0, [r1] - be16 r0 - exit", - ) - .unwrap(); - let mem = &mut [0x11, 0x22]; - let mut vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x1122); - } -} - -#[test] -fn test_jit_be16_high() { - let prog = assemble( - " - ldxdw r0, [r1] - be16 r0 - exit", - ) - .unwrap(); - let mem = &mut [0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88]; - let mut vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x1122); - } -} - -#[test] -fn test_jit_be32() { - let prog = assemble( - " - ldxw r0, [r1] - be32 r0 - exit", - ) - .unwrap(); - let mem = &mut [0x11, 0x22, 0x33, 0x44]; - let mut vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x11223344); - } -} - -#[test] -fn test_jit_be32_high() { - let prog = assemble( - " - ldxdw r0, [r1] - be32 r0 - exit", - ) - .unwrap(); - let mem = &mut [0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88]; - let mut vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x11223344); - } -} - -#[test] -fn test_jit_be64() { - let prog = assemble( - " - ldxdw r0, [r1] - be64 r0 - exit", - ) - .unwrap(); - let mem = &mut [0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88]; - let mut vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x1122334455667788); - } -} - -#[test] -fn test_jit_call() { - let prog = assemble( - " - mov r1, 1 - mov r2, 2 - mov r3, 3 - mov r4, 4 - mov r5, 5 - call 0 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.register_helper(0, helpers::gather_bytes).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x0102030405); - } -} - -#[test] -fn test_jit_call_memfrob() { - let prog = assemble( - " - mov r6, r1 - add r1, 2 - mov r2, 4 - call 1 - ldxdw r0, [r6] - be64 r0 - exit", - ) - .unwrap(); - let mem = &mut [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]; - let mut vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - vm.register_helper(1, helpers::memfrob).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x102292e2f2c0708); - } -} - -// TODO: helpers::trash_registers needs asm!(). -// Try this again once asm!() is available in stable. -//#[test] -//fn test_jit_call_save() { -//let prog = &[ -//0xb7, 0x06, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, -//0xb7, 0x07, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, -//0xb7, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, -//0xb7, 0x09, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, -//0x85, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, -//0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -//0x4f, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -//0x4f, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -//0x4f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -//0x4f, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -//0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -//]; -//let mut vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap(); -//vm.register_helper(2, helpers::trash_registers); -//vm.jit_compile().unwrap(); -//unsafe { assert_eq!(vm.execute_program_jit().unwrap(), 0x4321); } -//} - -#[test] -fn test_jit_div32_high_divisor() { - let prog = assemble( - " - mov r0, 12 - lddw r1, 0x100000004 - div32 r0, r1 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x3); - } -} - -#[test] -fn test_jit_div32_imm() { - let prog = assemble( - " - lddw r0, 0x10000000c - div32 r0, 4 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x3); - } -} - -#[test] -fn test_jit_div32_reg() { - let prog = assemble( - " - lddw r0, 0x10000000c - mov r1, 4 - div32 r0, r1 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x3); - } -} - -#[test] -fn test_jit_div64_imm() { - let prog = assemble( - " - mov r0, 0xc - lsh r0, 32 - div r0, 4 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x300000000); - } -} - -#[test] -fn test_jit_div64_reg() { - let prog = assemble( - " - mov r0, 0xc - lsh r0, 32 - mov r1, 4 - div r0, r1 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x300000000); - } -} - -// For some register numbers, we don't emit the same instructions for handling divisions by zero, -// which means we don't use the same offset to skip these instructions when the divisor is not -// zero. We've had a regression because of this before, make sure we test it. -#[test] -fn test_jit_div32_highreg() { - let prog = assemble( - " - mov r0, 2 - mov r7, 4 - div32 r7, r0 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x2); - } -} - -#[test] -fn test_jit_div64_highreg() { - let prog = assemble( - " - mov r0, 2 - mov r7, 4 - div r7, r0 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x2); - } -} - -#[test] -fn test_jit_early_exit() { - let prog = assemble( - " - mov r0, 3 - exit - mov r0, 4 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x3); - } -} - -// uBPF limits the number of user functions at 64. We don't. -//#[test] -//fn test_jit_err_call_bad_imm() { -//} - -#[test] -#[should_panic(expected = "[JIT] Error: unknown helper function (id: 0x3f)")] -fn test_jit_err_call_unreg() { - let prog = assemble( - " - mov r1, 1 - mov r2, 2 - mov r3, 3 - mov r4, 4 - mov r5, 5 - call 63 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - vm.execute_program_jit().unwrap(); - } -} - -#[test] -fn test_jit_div64_by_zero_imm() { - let prog = assemble( - " - mov32 r0, 1 - div r0, 0 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x0); - } -} - -#[test] -fn test_jit_div_by_zero_imm() { - let prog = assemble( - " - mov32 r0, 1 - div32 r0, 0 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x0); - } -} - -#[test] -fn test_jit_mod64_by_zero_imm() { - let prog = assemble( - " - mov32 r0, 1 - mod r0, 0 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_mod_by_zero_imm() { - let prog = assemble( - " - mov32 r0, 1 - mod32 r0, 0 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_div64_by_zero_reg() { - let prog = assemble( - " - mov32 r0, 1 - mov32 r1, 0 - div r0, r1 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x0); - } -} - -#[test] -fn test_jit_div_by_zero_reg() { - let prog = assemble( - " - mov32 r0, 1 - mov32 r1, 0 - div32 r0, r1 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x0); - } -} - -#[test] -fn test_jit_mod64_by_zero_reg() { - let prog = assemble( - " - mov32 r0, 1 - mov32 r1, 0 - mod r0, r1 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_mod_by_zero_reg() { - let prog = assemble( - " - mov32 r0, 1 - mov32 r1, 0 - mod32 r0, r1 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -// TODO SKIP: JIT disabled for this testcase (stack oob check not implemented) -// #[test] -// #[should_panic(expected = "Error: out of bounds memory store (insn #1)")] -// fn test_jit_err_stack_out_of_bound() { -// let prog = &[ -// 0x72, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -// ]; -// let mut vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap(); -// vm.jit_compile().unwrap(); -// unsafe { vm.execute_program_jit().unwrap(); } -// } - -#[test] -fn test_jit_exit() { - let prog = assemble( - " - mov r0, 0 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x0); - } -} - -#[test] -fn test_jit_ja() { - let prog = assemble( - " - mov r0, 1 - ja +1 - mov r0, 2 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_jeq_imm() { - let prog = assemble( - " - mov32 r0, 0 - mov32 r1, 0xa - jeq r1, 0xb, +4 - mov32 r0, 1 - mov32 r1, 0xb - jeq r1, 0xb, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_jeq_reg() { - let prog = assemble( - " - mov32 r0, 0 - mov32 r1, 0xa - mov32 r2, 0xb - jeq r1, r2, +4 - mov32 r0, 1 - mov32 r1, 0xb - jeq r1, r2, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_jge_imm() { - let prog = assemble( - " - mov32 r0, 0 - mov32 r1, 0xa - jge r1, 0xb, +4 - mov32 r0, 1 - mov32 r1, 0xc - jge r1, 0xb, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_jle_imm() { - let prog = assemble( - " - mov32 r0, 0 - mov32 r1, 5 - jle r1, 4, +1 - jle r1, 6, +1 - exit - jle r1, 5, +1 - exit - mov32 r0, 1 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_jle_reg() { - let prog = assemble( - " - mov r0, 0 - mov r1, 5 - mov r2, 4 - mov r3, 6 - jle r1, r2, +2 - jle r1, r1, +1 - exit - jle r1, r3, +1 - exit - mov r0, 1 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_jgt_imm() { - let prog = assemble( - " - mov32 r0, 0 - mov32 r1, 5 - jgt r1, 6, +2 - jgt r1, 5, +1 - jgt r1, 4, +1 - exit - mov32 r0, 1 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_jgt_reg() { - let prog = assemble( - " - mov r0, 0 - mov r1, 5 - mov r2, 6 - mov r3, 4 - jgt r1, r2, +2 - jgt r1, r1, +1 - jgt r1, r3, +1 - exit - mov r0, 1 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_jlt_imm() { - let prog = assemble( - " - mov32 r0, 0 - mov32 r1, 5 - jlt r1, 4, +2 - jlt r1, 5, +1 - jlt r1, 6, +1 - exit - mov32 r0, 1 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_jlt_reg() { - let prog = assemble( - " - mov r0, 0 - mov r1, 5 - mov r2, 4 - mov r3, 6 - jlt r1, r2, +2 - jlt r1, r1, +1 - jlt r1, r3, +1 - exit - mov r0, 1 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_jit_bounce() { - let prog = assemble( - " - mov r0, 1 - mov r6, r0 - mov r7, r6 - mov r8, r7 - mov r9, r8 - mov r0, r9 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_jne_reg() { - let prog = assemble( - " - mov32 r0, 0 - mov32 r1, 0xb - mov32 r2, 0xb - jne r1, r2, +4 - mov32 r0, 1 - mov32 r1, 0xa - jne r1, r2, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_jset_imm() { - let prog = assemble( - " - mov32 r0, 0 - mov32 r1, 0x7 - jset r1, 0x8, +4 - mov32 r0, 1 - mov32 r1, 0x9 - jset r1, 0x8, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_jset_reg() { - let prog = assemble( - " - mov32 r0, 0 - mov32 r1, 0x7 - mov32 r2, 0x8 - jset r1, r2, +4 - mov32 r0, 1 - mov32 r1, 0x9 - jset r1, r2, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_jsge_imm() { - let prog = assemble( - " - mov32 r0, 0 - mov r1, -2 - jsge r1, -1, +5 - jsge r1, 0, +4 - mov32 r0, 1 - mov r1, -1 - jsge r1, -1, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_jsge_reg() { - let prog = assemble( - " - mov32 r0, 0 - mov r1, -2 - mov r2, -1 - mov32 r3, 0 - jsge r1, r2, +5 - jsge r1, r3, +4 - mov32 r0, 1 - mov r1, r2 - jsge r1, r2, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_jsle_imm() { - let prog = assemble( - " - mov32 r0, 0 - mov r1, -2 - jsle r1, -3, +1 - jsle r1, -1, +1 - exit - mov32 r0, 1 - jsle r1, -2, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_jsle_reg() { - let prog = assemble( - " - mov32 r0, 0 - mov r1, -1 - mov r2, -2 - mov32 r3, 0 - jsle r1, r2, +1 - jsle r1, r3, +1 - exit - mov32 r0, 1 - mov r1, r2 - jsle r1, r2, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_jsgt_imm() { - let prog = assemble( - " - mov32 r0, 0 - mov r1, -2 - jsgt r1, -1, +4 - mov32 r0, 1 - mov32 r1, 0 - jsgt r1, -1, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_jsgt_reg() { - let prog = assemble( - " - mov32 r0, 0 - mov r1, -2 - mov r2, -1 - jsgt r1, r2, +4 - mov32 r0, 1 - mov32 r1, 0 - jsgt r1, r2, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_jslt_imm() { - let prog = assemble( - " - mov32 r0, 0 - mov r1, -2 - jslt r1, -3, +2 - jslt r1, -2, +1 - jslt r1, -1, +1 - exit - mov32 r0, 1 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_jslt_reg() { - let prog = assemble( - " - mov32 r0, 0 - mov r1, -2 - mov r2, -3 - mov r3, -1 - jslt r1, r1, +2 - jslt r1, r2, +1 - jslt r1, r3, +1 - exit - mov32 r0, 1 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_jeq32_imm() { - let prog = assemble( - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0x0 - mov32 r1, 0xa - jeq32 r1, 0xb, +5 - mov32 r0, 1 - mov r1, 0xb - or r1, r9 - jeq32 r1, 0xb, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_jeq32_reg() { - let prog = assemble( - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, 0xa - mov32 r2, 0xb - jeq32 r1, r2, +5 - mov32 r0, 1 - mov32 r1, 0xb - or r1, r9 - jeq32 r1, r2, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_jge32_imm() { - let prog = assemble( - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, 0xa - jge32 r1, 0xb, +5 - mov32 r0, 1 - or r1, r9 - mov32 r1, 0xc - jge32 r1, 0xb, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_jge32_reg() { - let prog = assemble( - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, 0xa - mov32 r2, 0xb - jge32 r1, r2, +5 - mov32 r0, 1 - or r1, r9 - mov32 r1, 0xc - jge32 r1, r2, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_jgt32_imm() { - let prog = assemble( - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, 5 - or r1, r9 - jgt32 r1, 6, +4 - jgt32 r1, 5, +3 - jgt32 r1, 4, +1 - exit - mov32 r0, 1 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_jgt32_reg() { - let prog = assemble( - " - mov r9, 1 - lsh r9, 32 - mov r0, 0 - mov r1, 5 - mov32 r1, 5 - or r1, r9 - mov r2, 6 - mov r3, 4 - jgt32 r1, r2, +4 - jgt32 r1, r1, +3 - jgt32 r1, r3, +1 - exit - mov r0, 1 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_jle32_imm() { - let prog = assemble( - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, 5 - or r1, r9 - jle32 r1, 4, +5 - jle32 r1, 6, +1 - exit - jle32 r1, 5, +1 - exit - mov32 r0, 1 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_jle32_reg() { - let prog = assemble( - " - mov r9, 1 - lsh r9, 32 - mov r0, 0 - mov r1, 5 - mov r2, 4 - mov r3, 6 - or r1, r9 - jle32 r1, r2, +5 - jle32 r1, r1, +1 - exit - jle32 r1, r3, +1 - exit - mov r0, 1 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_jlt32_imm() { - let prog = assemble( - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, 5 - or r1, r9 - jlt32 r1, 4, +4 - jlt32 r1, 5, +3 - jlt32 r1, 6, +1 - exit - mov32 r0, 1 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_jlt32_reg() { - let prog = assemble( - " - mov r9, 1 - lsh r9, 32 - mov r0, 0 - mov r1, 5 - mov r2, 4 - mov r3, 6 - or r1, r9 - jlt32 r1, r2, +4 - jlt32 r1, r1, +3 - jlt32 r1, r3, +1 - exit - mov r0, 1 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_jne32_imm() { - let prog = assemble( - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, 0xb - or r1, r9 - jne32 r1, 0xb, +4 - mov32 r0, 1 - mov32 r1, 0xa - or r1, r9 - jne32 r1, 0xb, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_jne32_reg() { - let prog = assemble( - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, 0xb - or r1, r9 - mov32 r2, 0xb - jne32 r1, r2, +4 - mov32 r0, 1 - mov32 r1, 0xa - or r1, r9 - jne32 r1, r2, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_jset32_imm() { - let prog = assemble( - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, 0x7 - or r1, r9 - jset32 r1, 0x8, +4 - mov32 r0, 1 - mov32 r1, 0x9 - jset32 r1, 0x8, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_jset32_reg() { - let prog = assemble( - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, 0x7 - or r1, r9 - mov32 r2, 0x8 - jset32 r1, r2, +4 - mov32 r0, 1 - mov32 r1, 0x9 - jset32 r1, r2, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_jsge32_imm() { - let prog = assemble( - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, -2 - or r1, r9 - jsge32 r1, -1, +5 - jsge32 r1, 0, +4 - mov32 r0, 1 - mov r1, -1 - jsge32 r1, -1, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_jsge32_reg() { - let prog = assemble( - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, -2 - or r1, r9 - mov r2, -1 - mov32 r3, 0 - jsge32 r1, r2, +5 - jsge32 r1, r3, +4 - mov32 r0, 1 - mov r1, r2 - jsge32 r1, r2, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_jsgt32_imm() { - let prog = assemble( - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, -2 - or r1, r9 - jsgt32 r1, -1, +4 - mov32 r0, 1 - mov32 r1, 0 - jsgt32 r1, -1, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_jsgt32_reg() { - let prog = assemble( - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, -2 - or r1, r9 - mov r2, -1 - jsgt32 r1, r2, +4 - mov32 r0, 1 - mov32 r1, 0 - jsgt32 r1, r2, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_jsle32_imm() { - let prog = assemble( - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, -2 - or r1, r9 - jsle32 r1, -3, +5 - jsle32 r1, -1, +1 - exit - mov32 r0, 1 - jsle32 r1, -2, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_jsle32_reg() { - let prog = assemble( - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, -2 - or r1, r9 - mov r2, -3 - mov32 r3, 0 - jsle32 r1, r2, +6 - jsle32 r1, r3, +1 - exit - mov32 r0, 1 - mov r1, r2 - jsle32 r1, r2, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_jslt32_imm() { - let prog = assemble( - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, -2 - or r1, r9 - jslt32 r1, -3, +4 - jslt32 r1, -2, +3 - jslt32 r1, -1, +1 - exit - mov32 r0, 1 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_jslt32_reg() { - let prog = assemble( - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, -2 - or r1, r9 - mov r2, -3 - mov r3, -1 - jslt32 r1, r1, +4 - jslt32 r1, r2, +3 - jslt32 r1, r3, +1 - exit - mov32 r0, 1 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_lddw() { - let prog = assemble( - " - lddw r0, 0x1122334455667788 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1122334455667788); - } -} - -#[test] -fn test_jit_lddw2() { - let prog = assemble( - " - lddw r0, 0x0000000080000000 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x80000000); - } -} - -#[test] -fn test_jit_ldxb_all() { - let prog = assemble( - " - mov r0, r1 - ldxb r9, [r0+0] - lsh r9, 0 - ldxb r8, [r0+1] - lsh r8, 4 - ldxb r7, [r0+2] - lsh r7, 8 - ldxb r6, [r0+3] - lsh r6, 12 - ldxb r5, [r0+4] - lsh r5, 16 - ldxb r4, [r0+5] - lsh r4, 20 - ldxb r3, [r0+6] - lsh r3, 24 - ldxb r2, [r0+7] - lsh r2, 28 - ldxb r1, [r0+8] - lsh r1, 32 - ldxb r0, [r0+9] - lsh r0, 36 - or r0, r1 - or r0, r2 - or r0, r3 - or r0, r4 - or r0, r5 - or r0, r6 - or r0, r7 - or r0, r8 - or r0, r9 - exit", - ) - .unwrap(); - let mem = &mut [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09]; - let mut vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x9876543210); - } -} - -#[test] -fn test_jit_ldxb() { - let prog = assemble( - " - ldxb r0, [r1+2] - exit", - ) - .unwrap(); - let mem = &mut [0xaa, 0xbb, 0x11, 0xcc, 0xdd]; - let mut vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x11); - } -} - -#[test] -fn test_jit_ldxdw() { - let prog = assemble( - " - ldxdw r0, [r1+2] - exit", - ) - .unwrap(); - let mem = &mut [ - 0xaa, 0xbb, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0xcc, 0xdd, - ]; - let mut vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x8877665544332211); - } -} - -#[test] -fn test_jit_ldxh_all() { - let prog = assemble( - " - mov r0, r1 - ldxh r9, [r0+0] - be16 r9 - lsh r9, 0 - ldxh r8, [r0+2] - be16 r8 - lsh r8, 4 - ldxh r7, [r0+4] - be16 r7 - lsh r7, 8 - ldxh r6, [r0+6] - be16 r6 - lsh r6, 12 - ldxh r5, [r0+8] - be16 r5 - lsh r5, 16 - ldxh r4, [r0+10] - be16 r4 - lsh r4, 20 - ldxh r3, [r0+12] - be16 r3 - lsh r3, 24 - ldxh r2, [r0+14] - be16 r2 - lsh r2, 28 - ldxh r1, [r0+16] - be16 r1 - lsh r1, 32 - ldxh r0, [r0+18] - be16 r0 - lsh r0, 36 - or r0, r1 - or r0, r2 - or r0, r3 - or r0, r4 - or r0, r5 - or r0, r6 - or r0, r7 - or r0, r8 - or r0, r9 - exit", - ) - .unwrap(); - let mem = &mut [ - 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x00, - 0x07, 0x00, 0x08, 0x00, 0x09, - ]; - let mut vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x9876543210); - } -} - -#[test] -fn test_jit_ldxh_all2() { - let prog = assemble( - " - mov r0, r1 - ldxh r9, [r0+0] - be16 r9 - ldxh r8, [r0+2] - be16 r8 - ldxh r7, [r0+4] - be16 r7 - ldxh r6, [r0+6] - be16 r6 - ldxh r5, [r0+8] - be16 r5 - ldxh r4, [r0+10] - be16 r4 - ldxh r3, [r0+12] - be16 r3 - ldxh r2, [r0+14] - be16 r2 - ldxh r1, [r0+16] - be16 r1 - ldxh r0, [r0+18] - be16 r0 - or r0, r1 - or r0, r2 - or r0, r3 - or r0, r4 - or r0, r5 - or r0, r6 - or r0, r7 - or r0, r8 - or r0, r9 - exit", - ) - .unwrap(); - let mem = &mut [ - 0x00, 0x01, 0x00, 0x02, 0x00, 0x04, 0x00, 0x08, 0x00, 0x10, 0x00, 0x20, 0x00, 0x40, 0x00, - 0x80, 0x01, 0x00, 0x02, 0x00, - ]; - let mut vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x3ff); - } -} - -#[test] -fn test_jit_ldxh() { - let prog = assemble( - " - ldxh r0, [r1+2] - exit", - ) - .unwrap(); - let mem = &mut [0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd]; - let mut vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x2211); - } -} - -#[test] -fn test_jit_ldxh_same_reg() { - let prog = assemble( - " - mov r0, r1 - sth [r0], 0x1234 - ldxh r0, [r0] - exit", - ) - .unwrap(); - let mem = &mut [0xff, 0xff]; - let mut vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x1234); - } -} - -#[test] -fn test_jit_ldxw_all() { - let prog = assemble( - " - mov r0, r1 - ldxw r9, [r0+0] - be32 r9 - ldxw r8, [r0+4] - be32 r8 - ldxw r7, [r0+8] - be32 r7 - ldxw r6, [r0+12] - be32 r6 - ldxw r5, [r0+16] - be32 r5 - ldxw r4, [r0+20] - be32 r4 - ldxw r3, [r0+24] - be32 r3 - ldxw r2, [r0+28] - be32 r2 - ldxw r1, [r0+32] - be32 r1 - ldxw r0, [r0+36] - be32 r0 - or r0, r1 - or r0, r2 - or r0, r3 - or r0, r4 - or r0, r5 - or r0, r6 - or r0, r7 - or r0, r8 - or r0, r9 - exit", - ) - .unwrap(); - let mem = &mut [ - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, - ]; - let mut vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x030f0f); - } -} - -#[test] -fn test_jit_ldxw() { - let prog = assemble( - " - ldxw r0, [r1+2] - exit", - ) - .unwrap(); - let mem = &mut [0xaa, 0xbb, 0x11, 0x22, 0x33, 0x44, 0xcc, 0xdd]; - let mut vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x44332211); - } -} - -#[test] -fn test_jit_le16() { - let prog = assemble( - " - ldxh r0, [r1] - le16 r0 - exit", - ) - .unwrap(); - let mem = &mut [0x22, 0x11]; - let mut vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x1122); - } -} - -#[test] -fn test_jit_le32() { - let prog = assemble( - " - ldxw r0, [r1] - le32 r0 - exit", - ) - .unwrap(); - let mem = &mut [0x44, 0x33, 0x22, 0x11]; - let mut vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x11223344); - } -} - -#[test] -fn test_jit_le64() { - let prog = assemble( - " - ldxdw r0, [r1] - le64 r0 - exit", - ) - .unwrap(); - let mem = &mut [0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11]; - let mut vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x1122334455667788); - } -} - -#[test] -fn test_jit_lsh_reg() { - let prog = assemble( - " - mov r0, 0x1 - mov r7, 4 - lsh r0, r7 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x10); - } -} - -#[test] -fn test_jit_mod() { - let prog = assemble( - " - mov32 r0, 5748 - mod32 r0, 92 - mov32 r1, 13 - mod32 r0, r1 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x5); - } -} - -#[test] -fn test_jit_mod32() { - let prog = assemble( - " - lddw r0, 0x100000003 - mod32 r0, 3 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x0); - } -} - -#[test] -fn test_jit_mod64() { - let prog = assemble( - " - mov32 r0, -1316649930 - lsh r0, 32 - or r0, 0x100dc5c8 - mov32 r1, 0xdde263e - lsh r1, 32 - or r1, 0x3cbef7f3 - mod r0, r1 - mod r0, 0x658f1778 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x30ba5a04); - } -} - -#[test] -fn test_jit_mov() { - let prog = assemble( - " - mov32 r1, 1 - mov32 r0, r1 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_mul32_imm() { - let prog = assemble( - " - mov r0, 3 - mul32 r0, 4 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0xc); - } -} - -#[test] -fn test_jit_mul32_reg() { - let prog = assemble( - " - mov r0, 3 - mov r1, 4 - mul32 r0, r1 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0xc); - } -} - -#[test] -fn test_jit_mul32_reg_overflow() { - let prog = assemble( - " - mov r0, 0x40000001 - mov r1, 4 - mul32 r0, r1 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x4); - } -} - -#[test] -fn test_jit_mul64_imm() { - let prog = assemble( - " - mov r0, 0x40000001 - mul r0, 4 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x100000004); - } -} - -#[test] -fn test_jit_mul64_reg() { - let prog = assemble( - " - mov r0, 0x40000001 - mov r1, 4 - mul r0, r1 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x100000004); - } -} - -#[test] -fn test_jit_mul_loop() { - let prog = assemble( - " - mov r0, 0x7 - add r1, 0xa - lsh r1, 0x20 - rsh r1, 0x20 - jeq r1, 0x0, +4 - mov r0, 0x7 - mul r0, 0x7 - add r1, -1 - jne r1, 0x0, -3 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x75db9c97); - } -} - -#[test] -fn test_jit_neg64() { - let prog = assemble( - " - mov32 r0, 2 - neg r0 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0xfffffffffffffffe); - } -} - -#[test] -fn test_jit_neg() { - let prog = assemble( - " - mov32 r0, 2 - neg32 r0 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0xfffffffe); - } -} - -#[test] -fn test_jit_prime() { - let prog = assemble( - " - mov r1, 67 - mov r0, 0x1 - mov r2, 0x2 - jgt r1, 0x2, +4 - ja +10 - add r2, 0x1 - mov r0, 0x1 - jge r2, r1, +7 - mov r3, r1 - div r3, r2 - mul r3, r2 - mov r4, r1 - sub r4, r3 - mov r0, 0x0 - jne r4, 0x0, -10 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_rhs32() { - let prog = assemble( - " - xor r0, r0 - sub r0, 1 - rsh32 r0, 8 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x00ffffff); - } -} - -#[test] -fn test_jit_rsh_reg() { - let prog = assemble( - " - mov r0, 0x10 - mov r7, 4 - rsh r0, r7 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x1); - } -} - -#[test] -fn test_jit_stack() { - let prog = assemble( - " - mov r1, 51 - stdw [r10-16], 0xab - stdw [r10-8], 0xcd - and r1, 1 - lsh r1, 3 - mov r2, r10 - add r2, r1 - ldxdw r0, [r2-16] - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0xcd); - } -} - -#[test] -fn test_jit_stack2() { - let prog = assemble( - " - stb [r10-4], 0x01 - stb [r10-3], 0x02 - stb [r10-2], 0x03 - stb [r10-1], 0x04 - mov r1, r10 - mov r2, 0x4 - sub r1, r2 - call 1 - mov r1, 0 - ldxb r2, [r10-4] - ldxb r3, [r10-3] - ldxb r4, [r10-2] - ldxb r5, [r10-1] - call 0 - xor r0, 0x2a2a2a2a - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.register_helper(0, helpers::gather_bytes).unwrap(); - vm.register_helper(1, helpers::memfrob).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x01020304); - } -} - -#[test] -fn test_jit_stb() { - let prog = assemble( - " - stb [r1+2], 0x11 - ldxb r0, [r1+2] - exit", - ) - .unwrap(); - let mem = &mut [0xaa, 0xbb, 0xff, 0xcc, 0xdd]; - let mut vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x11); - } -} - -#[test] -fn test_jit_stdw() { - let prog = assemble( - " - stdw [r1+2], 0x44332211 - ldxdw r0, [r1+2] - exit", - ) - .unwrap(); - let mem = &mut [ - 0xaa, 0xbb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcc, 0xdd, - ]; - let mut vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x44332211); - } -} - -#[test] -fn test_jit_sth() { - let prog = assemble( - " - sth [r1+2], 0x2211 - ldxh r0, [r1+2] - exit", - ) - .unwrap(); - let mem = &mut [0xaa, 0xbb, 0xff, 0xff, 0xcc, 0xdd]; - let mut vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x2211); - } -} - -#[test] -fn test_jit_string_stack() { - let prog = assemble( - " - mov r1, 0x78636261 - stxw [r10-8], r1 - mov r6, 0x0 - stxb [r10-4], r6 - stxb [r10-12], r6 - mov r1, 0x79636261 - stxw [r10-16], r1 - mov r1, r10 - add r1, -8 - mov r2, r1 - call 0x4 - mov r1, r0 - mov r0, 0x1 - lsh r1, 0x20 - rsh r1, 0x20 - jne r1, 0x0, +11 - mov r1, r10 - add r1, -8 - mov r2, r10 - add r2, -16 - call 0x4 - mov r1, r0 - lsh r1, 0x20 - rsh r1, 0x20 - mov r0, 0x1 - jeq r1, r6, +1 - mov r0, 0x0 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.register_helper(4, helpers::strcmp).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit().unwrap(), 0x0); - } -} - -#[test] -fn test_jit_stw() { - let prog = assemble( - " - stw [r1+2], 0x44332211 - ldxw r0, [r1+2] - exit", - ) - .unwrap(); - let mem = &mut [0xaa, 0xbb, 0xff, 0xff, 0xff, 0xff, 0xcc, 0xdd]; - let mut vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x44332211); - } -} - -#[test] -fn test_jit_stxb() { - let prog = assemble( - " - mov32 r2, 0x11 - stxb [r1+2], r2 - ldxb r0, [r1+2] - exit", - ) - .unwrap(); - let mem = &mut [0xaa, 0xbb, 0xff, 0xcc, 0xdd]; - let mut vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x11); - } -} - -#[test] -fn test_jit_stxb_all() { - let prog = assemble( - " - mov r0, 0xf0 - mov r2, 0xf2 - mov r3, 0xf3 - mov r4, 0xf4 - mov r5, 0xf5 - mov r6, 0xf6 - mov r7, 0xf7 - mov r8, 0xf8 - stxb [r1], r0 - stxb [r1+1], r2 - stxb [r1+2], r3 - stxb [r1+3], r4 - stxb [r1+4], r5 - stxb [r1+5], r6 - stxb [r1+6], r7 - stxb [r1+7], r8 - ldxdw r0, [r1] - be64 r0 - exit", - ) - .unwrap(); - let mem = &mut [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]; - let mut vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit(mem).unwrap(), 0xf0f2f3f4f5f6f7f8); - } -} - -#[test] -fn test_jit_stxb_all2() { - let prog = assemble( - " - mov r0, r1 - mov r1, 0xf1 - mov r9, 0xf9 - stxb [r0], r1 - stxb [r0+1], r9 - ldxh r0, [r0] - be16 r0 - exit", - ) - .unwrap(); - let mem = &mut [0xff, 0xff]; - let mut vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit(mem).unwrap(), 0xf1f9); - } -} - -#[test] -fn test_jit_stxb_chain() { - let prog = assemble( - " - mov r0, r1 - ldxb r9, [r0+0] - stxb [r0+1], r9 - ldxb r8, [r0+1] - stxb [r0+2], r8 - ldxb r7, [r0+2] - stxb [r0+3], r7 - ldxb r6, [r0+3] - stxb [r0+4], r6 - ldxb r5, [r0+4] - stxb [r0+5], r5 - ldxb r4, [r0+5] - stxb [r0+6], r4 - ldxb r3, [r0+6] - stxb [r0+7], r3 - ldxb r2, [r0+7] - stxb [r0+8], r2 - ldxb r1, [r0+8] - stxb [r0+9], r1 - ldxb r0, [r0+9] - exit", - ) - .unwrap(); - let mem = &mut [0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - let mut vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x2a); - } -} - -#[test] -fn test_jit_stxdw() { - let prog = assemble( - " - mov r2, -2005440939 - lsh r2, 32 - or r2, 0x44332211 - stxdw [r1+2], r2 - ldxdw r0, [r1+2] - exit", - ) - .unwrap(); - let mem = &mut [ - 0xaa, 0xbb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcc, 0xdd, - ]; - let mut vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x8877665544332211); - } -} - -#[test] -fn test_jit_stxh() { - let prog = assemble( - " - mov32 r2, 0x2211 - stxh [r1+2], r2 - ldxh r0, [r1+2] - exit", - ) - .unwrap(); - let mem = &mut [0xaa, 0xbb, 0xff, 0xff, 0xcc, 0xdd]; - let mut vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x2211); - } -} - -#[test] -fn test_jit_stxw() { - let prog = assemble( - " - mov32 r2, 0x44332211 - stxw [r1+2], r2 - ldxw r0, [r1+2] - exit", - ) - .unwrap(); - let mem = &mut [0xaa, 0xbb, 0xff, 0xff, 0xff, 0xff, 0xcc, 0xdd]; - let mut vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x44332211); - } -} - -#[test] -fn test_jit_subnet() { - let prog = assemble( - " - mov r2, 0xe - ldxh r3, [r1+12] - jne r3, 0x81, +2 - mov r2, 0x12 - ldxh r3, [r1+16] - and r3, 0xffff - jne r3, 0x8, +5 - add r1, r2 - mov r0, 0x1 - ldxw r1, [r1+16] - and r1, 0xffffff - jeq r1, 0x1a8c0, +1 - mov r0, 0x0 - exit", - ) - .unwrap(); - let mem = &mut [ - 0x00, 0x00, 0xc0, 0x9f, 0xa0, 0x97, 0x00, 0xa0, 0xcc, 0x3b, 0xbf, 0xfa, 0x08, 0x00, 0x45, - 0x10, 0x00, 0x3c, 0x46, 0x3c, 0x40, 0x00, 0x40, 0x06, 0x73, 0x1c, 0xc0, 0xa8, 0x01, 0x02, - 0xc0, 0xa8, 0x01, 0x01, 0x06, 0x0e, 0x00, 0x17, 0x99, 0xc5, 0xa0, 0xec, 0x00, 0x00, 0x00, - 0x00, 0xa0, 0x02, 0x7d, 0x78, 0xe0, 0xa3, 0x00, 0x00, 0x02, 0x04, 0x05, 0xb4, 0x04, 0x02, - 0x08, 0x0a, 0x00, 0x9c, 0x27, 0x24, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x00, - ]; - let mut vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x1); - } -} - -const PROG_TCP_PORT_80: [u8; 152] = [ - 0x71, 0x12, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x71, 0x13, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x67, 0x03, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x4f, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x03, 0x0c, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x71, 0x12, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x02, 0x0a, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x71, 0x12, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x01, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, - 0x57, 0x02, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x67, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x0f, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0x12, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x15, 0x02, 0x02, 0x00, 0x00, 0x50, 0x00, 0x00, 0x69, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x55, 0x01, 0x01, 0x00, 0x00, 0x50, 0x00, 0x00, 0xb7, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -]; - -#[test] -fn test_jit_tcp_port80_match() { - let mem = &mut [ - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x00, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x08, 0x00, 0x45, - 0x00, 0x00, 0x56, 0x00, 0x01, 0x00, 0x00, 0x40, 0x06, 0xf9, 0x4d, 0xc0, 0xa8, 0x00, 0x01, - 0xc0, 0xa8, 0x00, 0x02, 0x27, 0x10, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x50, 0x02, 0x20, 0x00, 0xc5, 0x18, 0x00, 0x00, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - ]; - let prog = &PROG_TCP_PORT_80; - let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x1); - } -} - -#[test] -fn test_jit_tcp_port80_nomatch() { - let mem = &mut [ - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x00, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x08, 0x00, 0x45, - 0x00, 0x00, 0x56, 0x00, 0x01, 0x00, 0x00, 0x40, 0x06, 0xf9, 0x4d, 0xc0, 0xa8, 0x00, 0x01, - 0xc0, 0xa8, 0x00, 0x02, 0x00, 0x16, 0x27, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x51, 0x02, 0x20, 0x00, 0xc5, 0x18, 0x00, 0x00, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - ]; - let prog = &PROG_TCP_PORT_80; - let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x0); - } -} - -#[test] -fn test_jit_tcp_port80_nomatch_ethertype() { - let mem = &mut [ - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x00, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x08, 0x01, 0x45, - 0x00, 0x00, 0x56, 0x00, 0x01, 0x00, 0x00, 0x40, 0x06, 0xf9, 0x4d, 0xc0, 0xa8, 0x00, 0x01, - 0xc0, 0xa8, 0x00, 0x02, 0x27, 0x10, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x50, 0x02, 0x20, 0x00, 0xc5, 0x18, 0x00, 0x00, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - ]; - let prog = &PROG_TCP_PORT_80; - let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x0); - } -} - -#[test] -fn test_jit_tcp_port80_nomatch_proto() { - let mem = &mut [ - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x00, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x08, 0x00, 0x45, - 0x00, 0x00, 0x56, 0x00, 0x01, 0x00, 0x00, 0x40, 0x11, 0xf9, 0x4d, 0xc0, 0xa8, 0x00, 0x01, - 0xc0, 0xa8, 0x00, 0x02, 0x27, 0x10, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x50, 0x02, 0x20, 0x00, 0xc5, 0x18, 0x00, 0x00, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - ]; - let prog = &PROG_TCP_PORT_80; - let mut vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit(mem).unwrap(), 0x0); - } -} - -#[test] -fn test_jit_tcp_sack_match() { - let mut mem = TCP_SACK_MATCH.to_vec(); - let prog = assemble(TCP_SACK_ASM).unwrap(); - let mut vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit(mem.as_mut_slice()).unwrap(), 0x1); - } -} - -#[test] -fn test_jit_tcp_sack_nomatch() { - let mut mem = TCP_SACK_NOMATCH.to_vec(); - let prog = assemble(TCP_SACK_ASM).unwrap(); - let mut vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - vm.jit_compile().unwrap(); - unsafe { - assert_eq!(vm.execute_program_jit(mem.as_mut_slice()).unwrap(), 0x0); - } -} diff --git a/kernel/crates/rbpf/tests/ubpf_verifier.rs b/kernel/crates/rbpf/tests/ubpf_verifier.rs deleted file mode 100644 index 8e6b03af..00000000 --- a/kernel/crates/rbpf/tests/ubpf_verifier.rs +++ /dev/null @@ -1,177 +0,0 @@ -// SPDX-License-Identifier: (Apache-2.0 OR MIT) -// Converted from the tests for uBPF -// Copyright 2015 Big Switch Networks, Inc -// Copyright 2016 6WIND S.A. - -// The tests contained in this file are extracted from the unit tests of uBPF software. Each test -// in this file has a name in the form `test_verifier_`, and corresponds to the -// (human-readable) code in `ubpf/tree/master/tests/`, available at -// (hyphen had to be replaced with underscores -// as Rust will not accept them in function names). It is strongly advised to refer to the uBPF -// version to understand what these program do. -// -// Each program was assembled from the uBPF version with the assembler provided by uBPF itself, and -// available at . -// The very few modifications that have been realized should be indicated. - -// These are unit tests for the eBPF “verifier”. - -extern crate rbpf; - -use rbpf::{assembler::assemble, ebpf}; - -#[test] -#[should_panic(expected = "[Verifier] Error: unsupported argument for LE/BE (insn #0)")] -fn test_verifier_err_endian_size() { - let prog = &[ - 0xdc, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xb7, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - ]; - let vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap(); - vm.execute_program().unwrap(); -} - -#[test] -#[should_panic(expected = "[Verifier] Error: incomplete LD_DW instruction (insn #0)")] -fn test_verifier_err_incomplete_lddw() { - // Note: ubpf has test-err-incomplete-lddw2, which is the same - let prog = &[ - 0x18, 0x00, 0x00, 0x00, 0x88, 0x77, 0x66, 0x55, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, - ]; - let vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap(); - vm.execute_program().unwrap(); -} - -#[test] -#[should_panic(expected = "[Verifier] Error: infinite loop")] -fn test_verifier_err_infinite_loop() { - let prog = assemble( - " - ja -1 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.execute_program().unwrap(); -} - -#[test] -#[should_panic(expected = "[Verifier] Error: invalid destination register (insn #0)")] -fn test_verifier_err_invalid_reg_dst() { - let prog = assemble( - " - mov r11, 1 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.execute_program().unwrap(); -} - -#[test] -#[should_panic(expected = "[Verifier] Error: invalid source register (insn #0)")] -fn test_verifier_err_invalid_reg_src() { - let prog = assemble( - " - mov r0, r11 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.execute_program().unwrap(); -} - -#[test] -#[should_panic(expected = "[Verifier] Error: jump to middle of LD_DW at #2 (insn #0)")] -fn test_verifier_err_jmp_lddw() { - let prog = assemble( - " - ja +1 - lddw r0, 0x1122334455667788 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.execute_program().unwrap(); -} - -#[test] -#[should_panic(expected = "[Verifier] Error: jump out of code to #3 (insn #0)")] -fn test_verifier_err_jmp_out() { - let prog = assemble( - " - ja +2 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.execute_program().unwrap(); -} - -#[test] -#[should_panic(expected = "[Verifier] Error: program does not end with “EXIT” instruction")] -fn test_verifier_err_no_exit() { - let prog = assemble( - " - mov32 r0, 0", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.execute_program().unwrap(); -} - -#[test] -fn test_verifier_err_no_exit_backward_jump() { - let prog = assemble( - " - ja +1 - exit - ja -2", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.execute_program().unwrap(); -} - -#[test] -#[should_panic(expected = "[Verifier] Error: eBPF program length limited to 1000000, here 1000001")] -fn test_verifier_err_too_many_instructions() { - // uBPF uses 65637 instructions, because it sets its limit at 65636. - // We use the classic 4096 limit from kernel, so no need to produce as many instructions. - let mut prog = (0..(1_000_000 * ebpf::INSN_SIZE)) - .map(|x| match x % 8 { - 0 => 0xb7, - 1 => 0x01, - _ => 0, - }) - .collect::>(); - prog.append(&mut vec![0x95, 0, 0, 0, 0, 0, 0, 0]); - - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.execute_program().unwrap(); -} - -#[test] -#[should_panic(expected = "[Verifier] Error: unknown eBPF opcode 0x6 (insn #0)")] -fn test_verifier_err_unknown_opcode() { - let prog = &[ - 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, - ]; - let vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap(); - vm.execute_program().unwrap(); -} - -#[test] -#[should_panic(expected = "[Verifier] Error: cannot write into register r10 (insn #0)")] -fn test_verifier_err_write_r10() { - let prog = assemble( - " - mov r10, 1 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.execute_program().unwrap(); -} diff --git a/kernel/crates/rbpf/tests/ubpf_vm.rs b/kernel/crates/rbpf/tests/ubpf_vm.rs deleted file mode 100644 index 4b2f1c36..00000000 --- a/kernel/crates/rbpf/tests/ubpf_vm.rs +++ /dev/null @@ -1,2674 +0,0 @@ -// SPDX-License-Identifier: (Apache-2.0 OR MIT) -// Converted from the tests for uBPF -// Copyright 2015 Big Switch Networks, Inc -// Copyright 2016 6WIND S.A. - -// The tests contained in this file are extracted from the unit tests of uBPF software. Each test -// in this file has a name in the form `test_vm_`, and corresponds to the (human-readable) -// code in `ubpf/tree/master/tests/`, available at -// (hyphen had to be replaced with underscores -// as Rust will not accept them in function names). It is strongly advised to refer to the uBPF -// version to understand what these program do. -// -// Each program was assembled from the uBPF version with the assembler provided by uBPF itself, and -// available at . -// The very few modifications that have been realized should be indicated. - -// These are unit tests for the eBPF interpreter. - -#![allow(clippy::unreadable_literal)] - -extern crate rbpf; -mod common; - -use common::{TCP_SACK_ASM, TCP_SACK_MATCH, TCP_SACK_NOMATCH}; -use rbpf::{assembler::assemble, helpers}; - -#[test] -fn test_vm_add() { - let prog = assemble( - " - mov32 r0, 0 - mov32 r1, 2 - add32 r0, 1 - add32 r0, r1 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x3); -} - -#[test] -fn test_vm_alu64_arith() { - let prog = assemble( - " - mov r0, 0 - mov r1, 1 - mov r2, 2 - mov r3, 3 - mov r4, 4 - mov r5, 5 - mov r6, 6 - mov r7, 7 - mov r8, 8 - mov r9, 9 - add r0, 23 - add r0, r7 - sub r0, 13 - sub r0, r1 - mul r0, 7 - mul r0, r3 - div r0, 2 - div r0, r4 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x2a); -} - -#[test] -fn test_vm_alu64_bit() { - let prog = assemble( - " - mov r0, 0 - mov r1, 1 - mov r2, 2 - mov r3, 3 - mov r4, 4 - mov r5, 5 - mov r6, 6 - mov r7, 7 - mov r8, 8 - or r0, r5 - or r0, 0xa0 - and r0, 0xa3 - mov r9, 0x91 - and r0, r9 - lsh r0, 32 - lsh r0, 22 - lsh r0, r8 - rsh r0, 32 - rsh r0, 19 - rsh r0, r7 - xor r0, 0x03 - xor r0, r2 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x11); -} - -#[test] -fn test_vm_alu_arith() { - let prog = assemble( - " - mov32 r0, 0 - mov32 r1, 1 - mov32 r2, 2 - mov32 r3, 3 - mov32 r4, 4 - mov32 r5, 5 - mov32 r6, 6 - mov32 r7, 7 - mov32 r8, 8 - mov32 r9, 9 - add32 r0, 23 - add32 r0, r7 - sub32 r0, 13 - sub32 r0, r1 - mul32 r0, 7 - mul32 r0, r3 - div32 r0, 2 - div32 r0, r4 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x2a); -} - -#[test] -fn test_vm_alu_bit() { - let prog = assemble( - " - mov32 r0, 0 - mov32 r1, 1 - mov32 r2, 2 - mov32 r3, 3 - mov32 r4, 4 - mov32 r5, 5 - mov32 r6, 6 - mov32 r7, 7 - mov32 r8, 8 - or32 r0, r5 - or32 r0, 0xa0 - and32 r0, 0xa3 - mov32 r9, 0x91 - and32 r0, r9 - lsh32 r0, 22 - lsh32 r0, r8 - rsh32 r0, 19 - rsh32 r0, r7 - xor32 r0, 0x03 - xor32 r0, r2 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x11); -} - -#[test] -fn test_vm_arsh32_high_shift() { - let prog = assemble( - " - mov r0, 8 - lddw r1, 0x100000001 - arsh32 r0, r1 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x4); -} - -#[test] -fn test_vm_arsh() { - let prog = assemble( - " - mov32 r0, 0xf8 - lsh32 r0, 28 - arsh32 r0, 16 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0xffff8000); -} - -#[test] -fn test_vm_arsh64() { - let prog = assemble( - " - mov32 r0, 1 - lsh r0, 63 - arsh r0, 55 - mov32 r1, 5 - arsh r0, r1 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0xfffffffffffffff8); -} - -#[test] -fn test_vm_arsh_reg() { - let prog = assemble( - " - mov32 r0, 0xf8 - mov32 r1, 16 - lsh32 r0, 28 - arsh32 r0, r1 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0xffff8000); -} - -#[test] -fn test_vm_arsh_imm_overflow() { - let prog = assemble( - " - mov r0, 1 - lsh r0, 63 - arsh r0, 0xff20 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0xffffffff80000000); -} - -#[test] -fn test_vm_arsh_reg_overflow() { - let prog = assemble( - " - mov r0, 1 - lsh r0, 63 - mov r1, 0xff04 - arsh r0, r1 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0xf800000000000000); -} - -#[test] -fn test_vm_arsh32_imm_overflow() { - let prog = assemble( - " - mov32 r0, 1 - lsh32 r0, 31 - arsh32 r0, 0xff10 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0xffff8000); -} - -#[test] -fn test_vm_arsh32_reg_overflow() { - let prog = assemble( - " - mov32 r0, 1 - lsh32 r0, 31 - mov32 r1, 32 - arsh32 r0, r1 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x80000000); -} - -#[test] -fn test_vm_be16() { - let prog = assemble( - " - ldxh r0, [r1] - be16 r0 - exit", - ) - .unwrap(); - let mem = &mut [0x11, 0x22]; - let vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program(mem).unwrap(), 0x1122); -} - -#[test] -fn test_vm_be16_high() { - let prog = assemble( - " - ldxdw r0, [r1] - be16 r0 - exit", - ) - .unwrap(); - let mem = &mut [0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88]; - let vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program(mem).unwrap(), 0x1122); -} - -#[test] -fn test_vm_be32() { - let prog = assemble( - " - ldxw r0, [r1] - be32 r0 - exit", - ) - .unwrap(); - let mem = &mut [0x11, 0x22, 0x33, 0x44]; - let vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program(mem).unwrap(), 0x11223344); -} - -#[test] -fn test_vm_be32_high() { - let prog = assemble( - " - ldxdw r0, [r1] - be32 r0 - exit", - ) - .unwrap(); - let mem = &mut [0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88]; - let vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program(mem).unwrap(), 0x11223344); -} - -#[test] -fn test_vm_be64() { - let prog = assemble( - " - ldxdw r0, [r1] - be64 r0 - exit", - ) - .unwrap(); - let mem = &mut [0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88]; - let vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program(mem).unwrap(), 0x1122334455667788); -} - -#[test] -fn test_vm_call() { - let prog = assemble( - " - mov r1, 1 - mov r2, 2 - mov r3, 3 - mov r4, 4 - mov r5, 5 - call 0 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.register_helper(0, helpers::gather_bytes).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x0102030405); -} - -#[test] -fn test_vm_call_memfrob() { - let prog = assemble( - " - mov r6, r1 - add r1, 2 - mov r2, 4 - call 1 - ldxdw r0, [r6] - be64 r0 - exit", - ) - .unwrap(); - let mem = &mut [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]; - let mut vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - vm.register_helper(1, helpers::memfrob).unwrap(); - assert_eq!(vm.execute_program(mem).unwrap(), 0x102292e2f2c0708); -} - -// TODO: helpers::trash_registers needs asm!(). -// Try this again once asm!() is available in stable. -//#[test] -//fn test_vm_call_save() { -//let prog = &[ -//0xb7, 0x06, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, -//0xb7, 0x07, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, -//0xb7, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, -//0xb7, 0x09, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, -//0x85, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, -//0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -//0x4f, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -//0x4f, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -//0x4f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -//0x4f, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -//0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -//]; -//let mut vm = rbpf::EbpfVmNoData::new(Some(prog)).unwrap(); -//vm.register_helper(2, helpers::trash_registers); -//assert_eq!(vm.execute_program().unwrap(), 0x4321); -//} - -#[test] -fn test_vm_div32_high_divisor() { - let prog = assemble( - " - mov r0, 12 - lddw r1, 0x100000004 - div32 r0, r1 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x3); -} - -#[test] -fn test_vm_div32_imm() { - let prog = assemble( - " - lddw r0, 0x10000000c - div32 r0, 4 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x3); -} - -#[test] -fn test_vm_div32_reg() { - let prog = assemble( - " - lddw r0, 0x10000000c - mov r1, 4 - div32 r0, r1 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x3); -} - -#[test] -fn test_vm_div64_imm() { - let prog = assemble( - " - mov r0, 0xc - lsh r0, 32 - div r0, 4 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x300000000); -} - -#[test] -fn test_vm_div64_reg() { - let prog = assemble( - " - mov r0, 0xc - lsh r0, 32 - mov r1, 4 - div r0, r1 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x300000000); -} - -#[test] -fn test_vm_early_exit() { - let prog = assemble( - " - mov r0, 3 - exit - mov r0, 4 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x3); -} - -// uBPF limits the number of user functions at 64. We don't. -//#[test] -//fn test_vm_err_call_bad_imm() { -//} - -#[test] -#[should_panic(expected = "Error: unknown helper function (id: 0x3f)")] -fn test_vm_err_call_unreg() { - let prog = assemble( - " - mov r1, 1 - mov r2, 2 - mov r3, 3 - mov r4, 4 - mov r5, 5 - call 63 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.execute_program().unwrap(); -} - -#[test] -fn test_vm_div64_by_zero_imm() { - let prog = assemble( - " - mov32 r0, 1 - div r0, 0 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x0); -} - -#[test] -fn test_vm_div_by_zero_imm() { - let prog = assemble( - " - mov32 r0, 1 - div32 r0, 0 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x0); -} - -#[test] -fn test_vm_mod64_by_zero_imm() { - let prog = assemble( - " - mov32 r0, 1 - mod r0, 0 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_mod_by_zero_imm() { - let prog = assemble( - " - mov32 r0, 1 - mod32 r0, 0 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -// Make sure we only consider the last 32 bits of the divisor. -#[test] -fn test_vm_mod_by_zero_reg_long() { - let prog = assemble( - " - lddw r1, 0x100000000 - mod32 r0, r1 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x0); -} - -#[test] -fn test_vm_div64_by_zero_reg() { - let prog = assemble( - " - mov32 r0, 1 - mov32 r1, 0 - div r0, r1 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x0); -} - -#[test] -fn test_vm_div_by_zero_reg() { - let prog = assemble( - " - mov32 r0, 1 - mov32 r1, 0 - div32 r0, r1 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x0); -} - -// Make sure we only consider the last 32 bits of the divisor. -#[test] -fn test_vm_div_by_zero_reg_long() { - let prog = assemble( - " - lddw r1, 0x100000000 - div32 r0, r1 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x0); -} - -#[test] -fn test_vm_mod64_by_zero_reg() { - let prog = assemble( - " - mov32 r0, 1 - mov32 r1, 0 - mod r0, r1 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_mod_by_zero_reg() { - let prog = assemble( - " - mov32 r0, 1 - mov32 r1, 0 - mod32 r0, r1 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -#[should_panic(expected = "Error: out of bounds memory store (insn #1)")] -fn test_vm_err_stack_out_of_bound() { - let prog = assemble( - " - stb [r10], 0 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.execute_program().unwrap(); -} - -#[test] -fn test_vm_exit() { - let prog = assemble( - " - mov r0, 0 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x0); -} - -#[test] -fn test_vm_ja() { - let prog = assemble( - " - mov r0, 1 - ja +1 - mov r0, 2 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_jeq_imm() { - let prog = assemble( - " - mov32 r0, 0 - mov32 r1, 0xa - jeq r1, 0xb, +4 - mov32 r0, 1 - mov32 r1, 0xb - jeq r1, 0xb, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_jeq_reg() { - let prog = assemble( - " - mov32 r0, 0 - mov32 r1, 0xa - mov32 r2, 0xb - jeq r1, r2, +4 - mov32 r0, 1 - mov32 r1, 0xb - jeq r1, r2, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_jge_imm() { - let prog = assemble( - " - mov32 r0, 0 - mov32 r1, 0xa - jge r1, 0xb, +4 - mov32 r0, 1 - mov32 r1, 0xc - jge r1, 0xb, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_jle_imm() { - let prog = assemble( - " - mov32 r0, 0 - mov32 r1, 5 - jle r1, 4, +1 - jle r1, 6, +1 - exit - jle r1, 5, +1 - exit - mov32 r0, 1 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_jle_reg() { - let prog = assemble( - " - mov r0, 0 - mov r1, 5 - mov r2, 4 - mov r3, 6 - jle r1, r2, +2 - jle r1, r1, +1 - exit - jle r1, r3, +1 - exit - mov r0, 1 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_jgt_imm() { - let prog = assemble( - " - mov32 r0, 0 - mov32 r1, 5 - jgt r1, 6, +2 - jgt r1, 5, +1 - jgt r1, 4, +1 - exit - mov32 r0, 1 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_jgt_reg() { - let prog = assemble( - " - mov r0, 0 - mov r1, 5 - mov r2, 6 - mov r3, 4 - jgt r1, r2, +2 - jgt r1, r1, +1 - jgt r1, r3, +1 - exit - mov r0, 1 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_jlt_imm() { - let prog = assemble( - " - mov32 r0, 0 - mov32 r1, 5 - jlt r1, 4, +2 - jlt r1, 5, +1 - jlt r1, 6, +1 - exit - mov32 r0, 1 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_jlt_reg() { - let prog = assemble( - " - mov r0, 0 - mov r1, 5 - mov r2, 4 - mov r3, 6 - jlt r1, r2, +2 - jlt r1, r1, +1 - jlt r1, r3, +1 - exit - mov r0, 1 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_jit_bounce() { - let prog = assemble( - " - mov r0, 1 - mov r6, r0 - mov r7, r6 - mov r8, r7 - mov r9, r8 - mov r0, r9 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_jne_reg() { - let prog = assemble( - " - mov32 r0, 0 - mov32 r1, 0xb - mov32 r2, 0xb - jne r1, r2, +4 - mov32 r0, 1 - mov32 r1, 0xa - jne r1, r2, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_jset_imm() { - let prog = assemble( - " - mov32 r0, 0 - mov32 r1, 0x7 - jset r1, 0x8, +4 - mov32 r0, 1 - mov32 r1, 0x9 - jset r1, 0x8, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_jset_reg() { - let prog = assemble( - " - mov32 r0, 0 - mov32 r1, 0x7 - mov32 r2, 0x8 - jset r1, r2, +4 - mov32 r0, 1 - mov32 r1, 0x9 - jset r1, r2, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_jsge_imm() { - let prog = assemble( - " - mov32 r0, 0 - mov r1, -2 - jsge r1, -1, +5 - jsge r1, 0, +4 - mov32 r0, 1 - mov r1, -1 - jsge r1, -1, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_jsge_reg() { - let prog = assemble( - " - mov32 r0, 0 - mov r1, -2 - mov r2, -1 - mov32 r3, 0 - jsge r1, r2, +5 - jsge r1, r3, +4 - mov32 r0, 1 - mov r1, r2 - jsge r1, r2, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_jsle_imm() { - let prog = assemble( - " - mov32 r0, 0 - mov r1, -2 - jsle r1, -3, +1 - jsle r1, -1, +1 - exit - mov32 r0, 1 - jsle r1, -2, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_jsle_reg() { - let prog = assemble( - " - mov32 r0, 0 - mov r1, -1 - mov r2, -2 - mov32 r3, 0 - jsle r1, r2, +1 - jsle r1, r3, +1 - exit - mov32 r0, 1 - mov r1, r2 - jsle r1, r2, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_jsgt_imm() { - let prog = assemble( - " - mov32 r0, 0 - mov r1, -2 - jsgt r1, -1, +4 - mov32 r0, 1 - mov32 r1, 0 - jsgt r1, -1, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_jsgt_reg() { - let prog = assemble( - " - mov32 r0, 0 - mov r1, -2 - mov r2, -1 - jsgt r1, r2, +4 - mov32 r0, 1 - mov32 r1, 0 - jsgt r1, r2, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_jslt_imm() { - let prog = assemble( - " - mov32 r0, 0 - mov r1, -2 - jslt r1, -3, +2 - jslt r1, -2, +1 - jslt r1, -1, +1 - exit - mov32 r0, 1 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_jslt_reg() { - let prog = assemble( - " - mov32 r0, 0 - mov r1, -2 - mov r2, -3 - mov r3, -1 - jslt r1, r1, +2 - jslt r1, r2, +1 - jslt r1, r3, +1 - exit - mov32 r0, 1 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_jeq32_imm() { - let prog = assemble( - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0x0 - mov32 r1, 0xa - jeq32 r1, 0xb, +5 - mov32 r0, 1 - mov r1, 0xb - or r1, r9 - jeq32 r1, 0xb, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_jeq32_reg() { - let prog = assemble( - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, 0xa - mov32 r2, 0xb - jeq32 r1, r2, +5 - mov32 r0, 1 - mov32 r1, 0xb - or r1, r9 - jeq32 r1, r2, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_jge32_imm() { - let prog = assemble( - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, 0xa - jge32 r1, 0xb, +5 - mov32 r0, 1 - or r1, r9 - mov32 r1, 0xc - jge32 r1, 0xb, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_jge32_reg() { - let prog = assemble( - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, 0xa - mov32 r2, 0xb - jge32 r1, r2, +5 - mov32 r0, 1 - or r1, r9 - mov32 r1, 0xc - jge32 r1, r2, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_jgt32_imm() { - let prog = assemble( - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, 5 - or r1, r9 - jgt32 r1, 6, +4 - jgt32 r1, 5, +3 - jgt32 r1, 4, +1 - exit - mov32 r0, 1 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_jgt32_reg() { - let prog = assemble( - " - mov r9, 1 - lsh r9, 32 - mov r0, 0 - mov r1, 5 - mov32 r1, 5 - or r1, r9 - mov r2, 6 - mov r3, 4 - jgt32 r1, r2, +4 - jgt32 r1, r1, +3 - jgt32 r1, r3, +1 - exit - mov r0, 1 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_jle32_imm() { - let prog = assemble( - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, 5 - or r1, r9 - jle32 r1, 4, +5 - jle32 r1, 6, +1 - exit - jle32 r1, 5, +1 - exit - mov32 r0, 1 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_jle32_reg() { - let prog = assemble( - " - mov r9, 1 - lsh r9, 32 - mov r0, 0 - mov r1, 5 - mov r2, 4 - mov r3, 6 - or r1, r9 - jle32 r1, r2, +5 - jle32 r1, r1, +1 - exit - jle32 r1, r3, +1 - exit - mov r0, 1 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_jlt32_imm() { - let prog = assemble( - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, 5 - or r1, r9 - jlt32 r1, 4, +4 - jlt32 r1, 5, +3 - jlt32 r1, 6, +1 - exit - mov32 r0, 1 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_jlt32_reg() { - let prog = assemble( - " - mov r9, 1 - lsh r9, 32 - mov r0, 0 - mov r1, 5 - mov r2, 4 - mov r3, 6 - or r1, r9 - jlt32 r1, r2, +4 - jlt32 r1, r1, +3 - jlt32 r1, r3, +1 - exit - mov r0, 1 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_jne32_imm() { - let prog = assemble( - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, 0xb - or r1, r9 - jne32 r1, 0xb, +4 - mov32 r0, 1 - mov32 r1, 0xa - or r1, r9 - jne32 r1, 0xb, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_jne32_reg() { - let prog = assemble( - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, 0xb - or r1, r9 - mov32 r2, 0xb - jne32 r1, r2, +4 - mov32 r0, 1 - mov32 r1, 0xa - or r1, r9 - jne32 r1, r2, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_jset32_imm() { - let prog = assemble( - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, 0x7 - or r1, r9 - jset32 r1, 0x8, +4 - mov32 r0, 1 - mov32 r1, 0x9 - jset32 r1, 0x8, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_jset32_reg() { - let prog = assemble( - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, 0x7 - or r1, r9 - mov32 r2, 0x8 - jset32 r1, r2, +4 - mov32 r0, 1 - mov32 r1, 0x9 - jset32 r1, r2, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_jsge32_imm() { - let prog = assemble( - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, -2 - or r1, r9 - jsge32 r1, -1, +5 - jsge32 r1, 0, +4 - mov32 r0, 1 - mov r1, -1 - jsge32 r1, -1, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_jsge32_reg() { - let prog = assemble( - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, -2 - or r1, r9 - mov r2, -1 - mov32 r3, 0 - jsge32 r1, r2, +5 - jsge32 r1, r3, +4 - mov32 r0, 1 - mov r1, r2 - jsge32 r1, r2, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_jsgt32_imm() { - let prog = assemble( - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, -2 - or r1, r9 - jsgt32 r1, -1, +4 - mov32 r0, 1 - mov32 r1, 0 - jsgt32 r1, -1, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_jsgt32_reg() { - let prog = assemble( - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, -2 - or r1, r9 - mov r2, -1 - jsgt32 r1, r2, +4 - mov32 r0, 1 - mov32 r1, 0 - jsgt32 r1, r2, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_jsle32_imm() { - let prog = assemble( - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, -2 - or r1, r9 - jsle32 r1, -3, +5 - jsle32 r1, -1, +1 - exit - mov32 r0, 1 - jsle32 r1, -2, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_jsle32_reg() { - let prog = assemble( - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, -2 - or r1, r9 - mov r2, -3 - mov32 r3, 0 - jsle32 r1, r2, +6 - jsle32 r1, r3, +1 - exit - mov32 r0, 1 - mov r1, r2 - jsle32 r1, r2, +1 - mov32 r0, 2 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_jslt32_imm() { - let prog = assemble( - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, -2 - or r1, r9 - jslt32 r1, -3, +4 - jslt32 r1, -2, +3 - jslt32 r1, -1, +1 - exit - mov32 r0, 1 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_jslt32_reg() { - let prog = assemble( - " - mov r9, 1 - lsh r9, 32 - mov32 r0, 0 - mov32 r1, -2 - or r1, r9 - mov r2, -3 - mov r3, -1 - jslt32 r1, r1, +4 - jslt32 r1, r2, +3 - jslt32 r1, r3, +1 - exit - mov32 r0, 1 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_lddw() { - let prog = assemble( - "lddw r0, 0x1122334455667788 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1122334455667788); -} - -#[test] -fn test_vm_lddw2() { - let prog = assemble( - " - lddw r0, 0x0000000080000000 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x80000000); -} - -#[test] -fn test_vm_ldxb_all() { - let prog = assemble( - " - mov r0, r1 - ldxb r9, [r0+0] - lsh r9, 0 - ldxb r8, [r0+1] - lsh r8, 4 - ldxb r7, [r0+2] - lsh r7, 8 - ldxb r6, [r0+3] - lsh r6, 12 - ldxb r5, [r0+4] - lsh r5, 16 - ldxb r4, [r0+5] - lsh r4, 20 - ldxb r3, [r0+6] - lsh r3, 24 - ldxb r2, [r0+7] - lsh r2, 28 - ldxb r1, [r0+8] - lsh r1, 32 - ldxb r0, [r0+9] - lsh r0, 36 - or r0, r1 - or r0, r2 - or r0, r3 - or r0, r4 - or r0, r5 - or r0, r6 - or r0, r7 - or r0, r8 - or r0, r9 - exit", - ) - .unwrap(); - let mem = &mut [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09]; - let vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program(mem).unwrap(), 0x9876543210); -} - -#[test] -fn test_vm_ldxb() { - let prog = assemble( - " - ldxb r0, [r1+2] - exit", - ) - .unwrap(); - let mem = &mut [0xaa, 0xbb, 0x11, 0xcc, 0xdd]; - let vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program(mem).unwrap(), 0x11); -} - -#[test] -fn test_vm_ldxdw() { - let prog = assemble( - " - ldxdw r0, [r1+2] - exit", - ) - .unwrap(); - let mem = &mut [ - 0xaa, 0xbb, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0xcc, 0xdd, - ]; - let vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program(mem).unwrap(), 0x8877665544332211); -} - -#[test] -fn test_vm_ldxh_all() { - let prog = assemble( - " - mov r0, r1 - ldxh r9, [r0+0] - be16 r9 - lsh r9, 0 - ldxh r8, [r0+2] - be16 r8 - lsh r8, 4 - ldxh r7, [r0+4] - be16 r7 - lsh r7, 8 - ldxh r6, [r0+6] - be16 r6 - lsh r6, 12 - ldxh r5, [r0+8] - be16 r5 - lsh r5, 16 - ldxh r4, [r0+10] - be16 r4 - lsh r4, 20 - ldxh r3, [r0+12] - be16 r3 - lsh r3, 24 - ldxh r2, [r0+14] - be16 r2 - lsh r2, 28 - ldxh r1, [r0+16] - be16 r1 - lsh r1, 32 - ldxh r0, [r0+18] - be16 r0 - lsh r0, 36 - or r0, r1 - or r0, r2 - or r0, r3 - or r0, r4 - or r0, r5 - or r0, r6 - or r0, r7 - or r0, r8 - or r0, r9 - exit", - ) - .unwrap(); - let mem = &mut [ - 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x00, - 0x07, 0x00, 0x08, 0x00, 0x09, - ]; - let vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program(mem).unwrap(), 0x9876543210); -} - -#[test] -fn test_vm_ldxh_all2() { - let prog = assemble( - " - mov r0, r1 - ldxh r9, [r0+0] - be16 r9 - ldxh r8, [r0+2] - be16 r8 - ldxh r7, [r0+4] - be16 r7 - ldxh r6, [r0+6] - be16 r6 - ldxh r5, [r0+8] - be16 r5 - ldxh r4, [r0+10] - be16 r4 - ldxh r3, [r0+12] - be16 r3 - ldxh r2, [r0+14] - be16 r2 - ldxh r1, [r0+16] - be16 r1 - ldxh r0, [r0+18] - be16 r0 - or r0, r1 - or r0, r2 - or r0, r3 - or r0, r4 - or r0, r5 - or r0, r6 - or r0, r7 - or r0, r8 - or r0, r9 - exit", - ) - .unwrap(); - let mem = &mut [ - 0x00, 0x01, 0x00, 0x02, 0x00, 0x04, 0x00, 0x08, 0x00, 0x10, 0x00, 0x20, 0x00, 0x40, 0x00, - 0x80, 0x01, 0x00, 0x02, 0x00, - ]; - let vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program(mem).unwrap(), 0x3ff); -} - -#[test] -fn test_vm_ldxh() { - let prog = assemble( - " - ldxh r0, [r1+2] - exit", - ) - .unwrap(); - let mem = &mut [0xaa, 0xbb, 0x11, 0x22, 0xcc, 0xdd]; - let vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program(mem).unwrap(), 0x2211); -} - -#[test] -fn test_vm_ldxh_same_reg() { - let prog = assemble( - " - mov r0, r1 - sth [r0], 0x1234 - ldxh r0, [r0] - exit", - ) - .unwrap(); - let mem = &mut [0xff, 0xff]; - let vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program(mem).unwrap(), 0x1234); -} - -#[test] -fn test_vm_ldxw_all() { - let prog = assemble( - " - mov r0, r1 - ldxw r9, [r0+0] - be32 r9 - ldxw r8, [r0+4] - be32 r8 - ldxw r7, [r0+8] - be32 r7 - ldxw r6, [r0+12] - be32 r6 - ldxw r5, [r0+16] - be32 r5 - ldxw r4, [r0+20] - be32 r4 - ldxw r3, [r0+24] - be32 r3 - ldxw r2, [r0+28] - be32 r2 - ldxw r1, [r0+32] - be32 r1 - ldxw r0, [r0+36] - be32 r0 - or r0, r1 - or r0, r2 - or r0, r3 - or r0, r4 - or r0, r5 - or r0, r6 - or r0, r7 - or r0, r8 - or r0, r9 - exit", - ) - .unwrap(); - let mem = &mut [ - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, - ]; - let vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program(mem).unwrap(), 0x030f0f); -} - -#[test] -fn test_vm_ldxw() { - let prog = assemble( - " - ldxw r0, [r1+2] - exit", - ) - .unwrap(); - let mem = &mut [0xaa, 0xbb, 0x11, 0x22, 0x33, 0x44, 0xcc, 0xdd]; - let vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program(mem).unwrap(), 0x44332211); -} - -#[test] -fn test_vm_le16() { - let prog = assemble( - " - ldxh r0, [r1] - le16 r0 - exit", - ) - .unwrap(); - let mem = &mut [0x22, 0x11]; - let vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program(mem).unwrap(), 0x1122); -} - -#[test] -fn test_vm_le32() { - let prog = assemble( - " - ldxw r0, [r1] - le32 r0 - exit", - ) - .unwrap(); - let mem = &mut [0x44, 0x33, 0x22, 0x11]; - let vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program(mem).unwrap(), 0x11223344); -} - -#[test] -fn test_vm_le64() { - let prog = assemble( - " - ldxdw r0, [r1] - le64 r0 - exit", - ) - .unwrap(); - let mem = &mut [0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11]; - let vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program(mem).unwrap(), 0x1122334455667788); -} - -#[test] -fn test_vm_lsh_imm() { - let prog = assemble( - " - mov r0, 1 - lsh r0, 4 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x10); -} - -#[test] -fn test_vm_lsh_reg() { - let prog = assemble( - " - mov r0, 1 - mov r7, 4 - lsh r0, r7 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x10); -} - -#[test] -fn test_vm_lsh32_imm() { - let prog = assemble( - " - mov32 r0, 1 - lsh32 r0, 4 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x10); -} - -#[test] -fn test_vm_lsh32_reg() { - let prog = assemble( - " - mov32 r0, 1 - mov32 r7, 4 - lsh32 r0, r7 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x10); -} - -#[test] -fn test_vm_lsh_imm_overflow() { - let prog = assemble( - " - mov r0, 1 - lsh r0, 64 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_lsh_reg_overflow() { - let prog = assemble( - " - mov r0, 1 - mov r7, 64 - lsh r0, r7 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_lsh32_imm_overflow() { - let prog = assemble( - " - mov32 r0, 1 - lsh32 r0, 32 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_lsh32_reg_overflow() { - let prog = assemble( - " - mov32 r0, 1 - mov32 r7, 32 - lsh32 r0, r7 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_mod() { - let prog = assemble( - " - mov32 r0, 5748 - mod32 r0, 92 - mov32 r1, 13 - mod32 r0, r1 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x5); -} - -#[test] -fn test_vm_mod32() { - let prog = assemble( - " - lddw r0, 0x100000003 - mod32 r0, 3 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x0); -} - -#[test] -fn test_vm_mod64() { - let prog = assemble( - " - mov32 r0, -1316649930 - lsh r0, 32 - or r0, 0x100dc5c8 - mov32 r1, 0xdde263e - lsh r1, 32 - or r1, 0x3cbef7f3 - mod r0, r1 - mod r0, 0x658f1778 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x30ba5a04); -} - -#[test] -fn test_vm_mov() { - let prog = assemble( - " - mov32 r1, 1 - mov32 r0, r1 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_mul32_imm() { - let prog = assemble( - " - mov r0, 3 - mul32 r0, 4 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0xc); -} - -#[test] -fn test_vm_mul32_reg() { - let prog = assemble( - " - mov r0, 3 - mov r1, 4 - mul32 r0, r1 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0xc); -} - -#[test] -fn test_vm_mul32_reg_overflow() { - let prog = assemble( - " - mov r0, 0x40000001 - mov r1, 4 - mul32 r0, r1 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x4); -} - -#[test] -fn test_vm_mul64_imm() { - let prog = assemble( - " - mov r0, 0x40000001 - mul r0, 4 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x100000004); -} - -#[test] -fn test_vm_mul64_reg() { - let prog = assemble( - " - mov r0, 0x40000001 - mov r1, 4 - mul r0, r1 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x100000004); -} - -#[test] -fn test_vm_mul_loop() { - let prog = assemble( - " - mov r0, 0x7 - add r1, 0xa - lsh r1, 0x20 - rsh r1, 0x20 - jeq r1, 0x0, +4 - mov r0, 0x7 - mul r0, 0x7 - add r1, -1 - jne r1, 0x0, -3 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x75db9c97); -} - -#[test] -fn test_vm_neg64() { - let prog = assemble( - " - mov32 r0, 2 - neg r0 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0xfffffffffffffffe); -} - -#[test] -fn test_vm_neg() { - let prog = assemble( - " - mov32 r0, 2 - neg32 r0 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0xfffffffe); -} - -#[test] -fn test_vm_prime() { - let prog = assemble( - " - mov r1, 67 - mov r0, 0x1 - mov r2, 0x2 - jgt r1, 0x2, +4 - ja +10 - add r2, 0x1 - mov r0, 0x1 - jge r2, r1, +7 - mov r3, r1 - div r3, r2 - mul r3, r2 - mov r4, r1 - sub r4, r3 - mov r0, 0x0 - jne r4, 0x0, -10 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_rhs32() { - let prog = assemble( - " - xor r0, r0 - sub r0, 1 - rsh32 r0, 8 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x00ffffff); -} - -#[test] -fn test_vm_rsh_reg() { - let prog = assemble( - " - mov r0, 0x10 - mov r7, 4 - rsh r0, r7 - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x1); -} - -#[test] -fn test_vm_stack() { - let prog = assemble( - " - mov r1, 51 - stdw [r10-16], 0xab - stdw [r10-8], 0xcd - and r1, 1 - lsh r1, 3 - mov r2, r10 - add r2, r1 - ldxdw r0, [r2-16] - exit", - ) - .unwrap(); - let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0xcd); -} - -#[test] -fn test_vm_stack2() { - let prog = assemble( - " - stb [r10-4], 0x01 - stb [r10-3], 0x02 - stb [r10-2], 0x03 - stb [r10-1], 0x04 - mov r1, r10 - mov r2, 0x4 - sub r1, r2 - call 1 - mov r1, 0 - ldxb r2, [r10-4] - ldxb r3, [r10-3] - ldxb r4, [r10-2] - ldxb r5, [r10-1] - call 0 - xor r0, 0x2a2a2a2a - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.register_helper(0, helpers::gather_bytes).unwrap(); - vm.register_helper(1, helpers::memfrob).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x01020304); -} - -#[test] -fn test_vm_stb() { - let prog = assemble( - " - stb [r1+2], 0x11 - ldxb r0, [r1+2] - exit", - ) - .unwrap(); - let mem = &mut [0xaa, 0xbb, 0xff, 0xcc, 0xdd]; - let vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program(mem).unwrap(), 0x11); -} - -#[test] -fn test_vm_stdw() { - let prog = assemble( - " - stdw [r1+2], 0x44332211 - ldxdw r0, [r1+2] - exit", - ) - .unwrap(); - let mem = &mut [ - 0xaa, 0xbb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcc, 0xdd, - ]; - let vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program(mem).unwrap(), 0x44332211); -} - -// If this case is not handled properly in check_mem(), then we may overflow when adding the -// context address and the offset, and make the thread panic with "attempt to add with overflow". -// Check that we panic with the expected out-of-bounds error. -// -// The new toolchain introduced `assert_unsafe_precondition` which panics with a different message and can't be -// caught by `#[should_panic]`. This is why we use `#[ignore]` here. -#[test] -#[should_panic(expected = "Error: out of bounds memory store (insn #1)")] -#[ignore] -fn test_vm_stdw_add_overflow() { - let prog = assemble( - " - stdw [r2-0x1], 0x44332211 - ldxw r0, [r1+2] - exit", - ) - .unwrap(); - let mem = &mut [ - 0xaa, 0xbb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcc, 0xdd, - ]; - let mut vm = rbpf::EbpfVmFixedMbuff::new(Some(&prog), 0x00, 0x10).unwrap(); - _ = vm.execute_program(mem).unwrap(); -} - -#[test] -fn test_vm_sth() { - let prog = assemble( - " - sth [r1+2], 0x2211 - ldxh r0, [r1+2] - exit", - ) - .unwrap(); - let mem = &mut [0xaa, 0xbb, 0xff, 0xff, 0xcc, 0xdd]; - let vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program(mem).unwrap(), 0x2211); -} - -#[test] -fn test_vm_string_stack() { - let prog = assemble( - " - mov r1, 0x78636261 - stxw [r10-8], r1 - mov r6, 0x0 - stxb [r10-4], r6 - stxb [r10-12], r6 - mov r1, 0x79636261 - stxw [r10-16], r1 - mov r1, r10 - add r1, -8 - mov r2, r1 - call 0x4 - mov r1, r0 - mov r0, 0x1 - lsh r1, 0x20 - rsh r1, 0x20 - jne r1, 0x0, +11 - mov r1, r10 - add r1, -8 - mov r2, r10 - add r2, -16 - call 0x4 - mov r1, r0 - lsh r1, 0x20 - rsh r1, 0x20 - mov r0, 0x1 - jeq r1, r6, +1 - mov r0, 0x0 - exit", - ) - .unwrap(); - let mut vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap(); - vm.register_helper(4, helpers::strcmp).unwrap(); - assert_eq!(vm.execute_program().unwrap(), 0x0); -} - -#[test] -fn test_vm_stw() { - let prog = assemble( - " - stw [r1+2], 0x44332211 - ldxw r0, [r1+2] - exit", - ) - .unwrap(); - let mem = &mut [0xaa, 0xbb, 0xff, 0xff, 0xff, 0xff, 0xcc, 0xdd]; - let vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program(mem).unwrap(), 0x44332211); -} - -#[test] -fn test_vm_stxb() { - let prog = assemble( - " - mov32 r2, 0x11 - stxb [r1+2], r2 - ldxb r0, [r1+2] - exit", - ) - .unwrap(); - let mem = &mut [0xaa, 0xbb, 0xff, 0xcc, 0xdd]; - let vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program(mem).unwrap(), 0x11); -} - -#[test] -fn test_vm_stxb_all() { - let prog = assemble( - " - mov r0, 0xf0 - mov r2, 0xf2 - mov r3, 0xf3 - mov r4, 0xf4 - mov r5, 0xf5 - mov r6, 0xf6 - mov r7, 0xf7 - mov r8, 0xf8 - stxb [r1], r0 - stxb [r1+1], r2 - stxb [r1+2], r3 - stxb [r1+3], r4 - stxb [r1+4], r5 - stxb [r1+5], r6 - stxb [r1+6], r7 - stxb [r1+7], r8 - ldxdw r0, [r1] - be64 r0 - exit", - ) - .unwrap(); - let mem = &mut [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]; - let vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program(mem).unwrap(), 0xf0f2f3f4f5f6f7f8); -} - -#[test] -fn test_vm_stxb_all2() { - let prog = assemble( - " - mov r0, r1 - mov r1, 0xf1 - mov r9, 0xf9 - stxb [r0], r1 - stxb [r0+1], r9 - ldxh r0, [r0] - be16 r0 - exit", - ) - .unwrap(); - let mem = &mut [0xff, 0xff]; - let vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program(mem).unwrap(), 0xf1f9); -} - -#[test] -fn test_vm_stxb_chain() { - let prog = assemble( - " - mov r0, r1 - ldxb r9, [r0+0] - stxb [r0+1], r9 - ldxb r8, [r0+1] - stxb [r0+2], r8 - ldxb r7, [r0+2] - stxb [r0+3], r7 - ldxb r6, [r0+3] - stxb [r0+4], r6 - ldxb r5, [r0+4] - stxb [r0+5], r5 - ldxb r4, [r0+5] - stxb [r0+6], r4 - ldxb r3, [r0+6] - stxb [r0+7], r3 - ldxb r2, [r0+7] - stxb [r0+8], r2 - ldxb r1, [r0+8] - stxb [r0+9], r1 - ldxb r0, [r0+9] - exit", - ) - .unwrap(); - let mem = &mut [0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - let vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program(mem).unwrap(), 0x2a); -} - -#[test] -fn test_vm_stxdw() { - let prog = assemble( - " - mov r2, -2005440939 - lsh r2, 32 - or r2, 0x44332211 - stxdw [r1+2], r2 - ldxdw r0, [r1+2] - exit", - ) - .unwrap(); - let mem = &mut [ - 0xaa, 0xbb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xcc, 0xdd, - ]; - let vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program(mem).unwrap(), 0x8877665544332211); -} - -#[test] -fn test_vm_stxh() { - let prog = assemble( - " - mov32 r2, 0x2211 - stxh [r1+2], r2 - ldxh r0, [r1+2] - exit", - ) - .unwrap(); - let mem = &mut [0xaa, 0xbb, 0xff, 0xff, 0xcc, 0xdd]; - let vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program(mem).unwrap(), 0x2211); -} - -#[test] -fn test_vm_stxw() { - let prog = assemble( - " - mov32 r2, 0x44332211 - stxw [r1+2], r2 - ldxw r0, [r1+2] - exit", - ) - .unwrap(); - let mem = &mut [0xaa, 0xbb, 0xff, 0xff, 0xff, 0xff, 0xcc, 0xdd]; - let vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program(mem).unwrap(), 0x44332211); -} - -#[test] -fn test_vm_subnet() { - let prog = assemble( - " - mov r2, 0xe - ldxh r3, [r1+12] - jne r3, 0x81, +2 - mov r2, 0x12 - ldxh r3, [r1+16] - and r3, 0xffff - jne r3, 0x8, +5 - add r1, r2 - mov r0, 0x1 - ldxw r1, [r1+16] - and r1, 0xffffff - jeq r1, 0x1a8c0, +1 - mov r0, 0x0 - exit", - ) - .unwrap(); - let mem = &mut [ - 0x00, 0x00, 0xc0, 0x9f, 0xa0, 0x97, 0x00, 0xa0, 0xcc, 0x3b, 0xbf, 0xfa, 0x08, 0x00, 0x45, - 0x10, 0x00, 0x3c, 0x46, 0x3c, 0x40, 0x00, 0x40, 0x06, 0x73, 0x1c, 0xc0, 0xa8, 0x01, 0x02, - 0xc0, 0xa8, 0x01, 0x01, 0x06, 0x0e, 0x00, 0x17, 0x99, 0xc5, 0xa0, 0xec, 0x00, 0x00, 0x00, - 0x00, 0xa0, 0x02, 0x7d, 0x78, 0xe0, 0xa3, 0x00, 0x00, 0x02, 0x04, 0x05, 0xb4, 0x04, 0x02, - 0x08, 0x0a, 0x00, 0x9c, 0x27, 0x24, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x03, 0x00, - ]; - let vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program(mem).unwrap(), 0x1); -} - -const PROG_TCP_PORT_80: [u8; 152] = [ - 0x71, 0x12, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x71, 0x13, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x67, 0x03, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x4f, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x03, 0x0c, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x71, 0x12, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0x02, 0x0a, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x71, 0x12, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x01, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, - 0x57, 0x02, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x67, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x0f, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0x12, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x15, 0x02, 0x02, 0x00, 0x00, 0x50, 0x00, 0x00, 0x69, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x55, 0x01, 0x01, 0x00, 0x00, 0x50, 0x00, 0x00, 0xb7, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -]; - -#[test] -fn test_vm_tcp_port80_match() { - let mem = &mut [ - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x00, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x08, 0x00, 0x45, - 0x00, 0x00, 0x56, 0x00, 0x01, 0x00, 0x00, 0x40, 0x06, 0xf9, 0x4d, 0xc0, 0xa8, 0x00, 0x01, - 0xc0, 0xa8, 0x00, 0x02, 0x27, 0x10, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x50, 0x02, 0x20, 0x00, 0xc5, 0x18, 0x00, 0x00, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - ]; - let prog = &PROG_TCP_PORT_80; - let vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap(); - assert_eq!(vm.execute_program(mem).unwrap(), 0x1); -} - -#[test] -fn test_vm_tcp_port80_nomatch() { - let mem = &mut [ - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x00, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x08, 0x00, 0x45, - 0x00, 0x00, 0x56, 0x00, 0x01, 0x00, 0x00, 0x40, 0x06, 0xf9, 0x4d, 0xc0, 0xa8, 0x00, 0x01, - 0xc0, 0xa8, 0x00, 0x02, 0x00, 0x16, 0x27, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x51, 0x02, 0x20, 0x00, 0xc5, 0x18, 0x00, 0x00, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - ]; - let prog = &PROG_TCP_PORT_80; - let vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap(); - assert_eq!(vm.execute_program(mem).unwrap(), 0x0); -} - -#[test] -fn test_vm_tcp_port80_nomatch_ethertype() { - let mem = &mut [ - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x00, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x08, 0x01, 0x45, - 0x00, 0x00, 0x56, 0x00, 0x01, 0x00, 0x00, 0x40, 0x06, 0xf9, 0x4d, 0xc0, 0xa8, 0x00, 0x01, - 0xc0, 0xa8, 0x00, 0x02, 0x27, 0x10, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x50, 0x02, 0x20, 0x00, 0xc5, 0x18, 0x00, 0x00, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - ]; - let prog = &PROG_TCP_PORT_80; - let vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap(); - assert_eq!(vm.execute_program(mem).unwrap(), 0x0); -} - -#[test] -fn test_vm_tcp_port80_nomatch_proto() { - let mem = &mut [ - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x00, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x08, 0x00, 0x45, - 0x00, 0x00, 0x56, 0x00, 0x01, 0x00, 0x00, 0x40, 0x11, 0xf9, 0x4d, 0xc0, 0xa8, 0x00, 0x01, - 0xc0, 0xa8, 0x00, 0x02, 0x27, 0x10, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x50, 0x02, 0x20, 0x00, 0xc5, 0x18, 0x00, 0x00, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, - ]; - let prog = &PROG_TCP_PORT_80; - let vm = rbpf::EbpfVmRaw::new(Some(prog)).unwrap(); - assert_eq!(vm.execute_program(mem).unwrap(), 0x0); -} - -#[test] -fn test_vm_tcp_sack_match() { - let mut mem = TCP_SACK_MATCH.to_vec(); - let prog = assemble(TCP_SACK_ASM).unwrap(); - let vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program(mem.as_mut_slice()).unwrap(), 0x1); -} - -#[test] -fn test_vm_tcp_sack_nomatch() { - let mut mem = TCP_SACK_NOMATCH.to_vec(); - let prog = assemble(TCP_SACK_ASM).unwrap(); - let vm = rbpf::EbpfVmRaw::new(Some(&prog)).unwrap(); - assert_eq!(vm.execute_program(mem.as_mut_slice()).unwrap(), 0x0); -} diff --git a/kernel/src/perf/kprobe.rs b/kernel/src/perf/kprobe.rs index 28a70620..7d99689c 100644 --- a/kernel/src/perf/kprobe.rs +++ b/kernel/src/perf/kprobe.rs @@ -11,7 +11,7 @@ use crate::filesystem::vfs::{FilePrivateData, FileSystem, IndexNode}; use crate::libs::casting::DowncastArc; use crate::libs::spinlock::SpinLockGuard; use crate::perf::util::PerfProbeArgs; -use crate::perf::PerfEventOps; +use crate::perf::{BasicPerfEbpfCallBack, PerfEventOps}; use alloc::boxed::Box; use alloc::string::String; use alloc::sync::Arc; @@ -19,7 +19,7 @@ use alloc::vec::Vec; use core::any::Any; use core::fmt::Debug; use kprobe::{CallBackFunc, ProbeArgs}; -use rbpf::EbpfVmRawOwned; +use rbpf::EbpfVmRaw; use system_error::SystemError; #[derive(Debug)] pub struct KprobePerfEvent { @@ -40,33 +40,49 @@ impl KprobePerfEvent { .downcast_arc::() .ok_or(SystemError::EINVAL)?; let prog_slice = file.insns(); - let mut vm = EbpfVmRawOwned::new(Some(prog_slice.to_vec())).map_err(|e| { + + let prog_slice = + unsafe { core::slice::from_raw_parts(prog_slice.as_ptr(), prog_slice.len()) }; + let mut vm = EbpfVmRaw::new(Some(prog_slice)).map_err(|e| { log::error!("create ebpf vm failed: {:?}", e); SystemError::EINVAL })?; - vm.register_helper_set(BPF_HELPER_FUN_SET.get()) - .map_err(|_| SystemError::EINVAL)?; + + for (id, f) in BPF_HELPER_FUN_SET.get() { + vm.register_helper(*id, *f) + .map_err(|_| SystemError::EINVAL)?; + } + // create a callback to execute the ebpf prog - let callback = Box::new(KprobePerfCallBack::new(file, vm)); + let callback; + + #[cfg(target_arch = "x86_64")] + { + use crate::perf::JITMem; + + log::info!("Using JIT compilation for BPF program on x86_64 architecture"); + let jit_mem = Box::new(JITMem::new()); + let jit_mem = Box::leak(jit_mem); + let jit_mem_addr = core::ptr::from_ref::(jit_mem) as usize; + vm.set_jit_exec_memory(jit_mem).unwrap(); + vm.jit_compile().unwrap(); + let basic_callback = BasicPerfEbpfCallBack::new(file, vm, jit_mem_addr); + callback = Box::new(KprobePerfCallBack(basic_callback)); + } + #[cfg(not(target_arch = "x86_64"))] + { + vm.register_allowed_memory(0..u64::MAX); + let basic_callback = BasicPerfEbpfCallBack::new(file, vm); + callback = Box::new(KprobePerfCallBack(basic_callback)); + } + // update callback for kprobe self.kprobe.write().update_event_callback(callback); Ok(()) } } -pub struct KprobePerfCallBack { - _bpf_prog_file: Arc, - vm: EbpfVmRawOwned, -} - -impl KprobePerfCallBack { - fn new(bpf_prog_file: Arc, vm: EbpfVmRawOwned) -> Self { - Self { - _bpf_prog_file: bpf_prog_file, - vm, - } - } -} +pub struct KprobePerfCallBack(BasicPerfEbpfCallBack); impl CallBackFunc for KprobePerfCallBack { fn call(&self, trap_frame: &dyn ProbeArgs) { @@ -78,10 +94,7 @@ impl CallBackFunc for KprobePerfCallBack { size_of::(), ) }; - let res = self.vm.execute_program(probe_context); - if res.is_err() { - log::error!("kprobe callback error: {:?}", res); - } + self.0.call(probe_context); } } diff --git a/kernel/src/perf/mod.rs b/kernel/src/perf/mod.rs index da39b395..cf8fd9b9 100644 --- a/kernel/src/perf/mod.rs +++ b/kernel/src/perf/mod.rs @@ -3,6 +3,8 @@ mod kprobe; mod tracepoint; mod util; +use crate::arch::MMArch; +use crate::bpf::prog::BpfProg; use crate::filesystem::epoll::{event_poll::EventPoll, EPollEventType, EPollItem}; use crate::filesystem::page_cache::PageCache; use crate::filesystem::vfs::file::{File, FileMode}; @@ -15,8 +17,11 @@ use crate::include::bindings::linux_bpf::{ }; use crate::libs::casting::DowncastArc; use crate::libs::spinlock::{SpinLock, SpinLockGuard}; +use crate::mm::allocator::page_frame::{ + allocate_page_frames, deallocate_page_frames, PageFrameCount, PhysPageFrame, +}; use crate::mm::fault::{PageFaultHandler, PageFaultMessage}; -use crate::mm::VmFaultReason; +use crate::mm::{MemoryManagementArch, VirtAddr, VmFaultReason}; use crate::perf::bpf::BpfPerfEvent; use crate::perf::util::{PerfEventIoc, PerfEventOpenFlags, PerfProbeArgs, PerfProbeConfig}; use crate::process::ProcessManager; @@ -30,10 +35,11 @@ use alloc::vec::Vec; use core::any::Any; use core::ffi::c_void; use core::fmt::Debug; -use core::ops::Deref; +use core::ops::{Deref, DerefMut}; use intertrait::{CastFrom, CastFromSync}; use log::info; use num_traits::FromPrimitive; +use rbpf::EbpfVmRaw; use system_error::SystemError; type Result = core::result::Result; @@ -55,6 +61,103 @@ pub trait PerfEventOps: Send + Sync + Debug + CastFromSync + CastFrom + IndexNod fn readable(&self) -> bool; } +pub struct JITMem { + virt_addr: VirtAddr, +} + +impl JITMem { + pub fn new() -> Self { + let vaddr = unsafe { + let (paddr, _count) = + allocate_page_frames(PageFrameCount::new(1)).expect("JITMem alloc failed"); + MMArch::phys_2_virt(paddr).unwrap() + }; + Self { virt_addr: vaddr } + } +} + +impl Deref for JITMem { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + unsafe { + let ptr = self.virt_addr.as_ptr(); + core::slice::from_raw_parts(ptr, 4096) + } + } +} + +impl DerefMut for JITMem { + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { + let ptr = self.virt_addr.as_ptr(); + core::slice::from_raw_parts_mut(ptr, 4096) + } + } +} + +impl Drop for JITMem { + fn drop(&mut self) { + unsafe { + let paddr = MMArch::virt_2_phys(self.virt_addr).expect("JITMem drop failed"); + let count = PageFrameCount::new(1); + deallocate_page_frames(PhysPageFrame::new(paddr), count); + } + } +} + +pub struct BasicPerfEbpfCallBack { + _bpf_prog_file: Arc, + vm: EbpfVmRaw<'static>, + #[cfg(target_arch = "x86_64")] + jit_mem_ptr: usize, +} + +unsafe impl Send for BasicPerfEbpfCallBack {} +unsafe impl Sync for BasicPerfEbpfCallBack {} + +impl BasicPerfEbpfCallBack { + #[cfg(not(target_arch = "x86_64"))] + fn new(bpf_prog_file: Arc, vm: EbpfVmRaw<'static>) -> Self { + Self { + _bpf_prog_file: bpf_prog_file, + vm, + } + } + #[cfg(target_arch = "x86_64")] + fn new(bpf_prog_file: Arc, vm: EbpfVmRaw<'static>, jit_mem_ptr: usize) -> Self { + Self { + _bpf_prog_file: bpf_prog_file, + vm, + jit_mem_ptr, + } + } + + pub fn call(&self, entry: &mut [u8]) { + let res = if cfg!(target_arch = "x86_64") { + unsafe { self.vm.execute_program_jit(entry) } + } else { + self.vm.execute_program(entry) + }; + if res.is_err() { + log::error!("kprobe callback error: {:?}", res); + } + } +} + +impl Drop for BasicPerfEbpfCallBack { + fn drop(&mut self) { + #[cfg(target_arch = "x86_64")] + { + unsafe { + let jit_mem = &mut *(self.jit_mem_ptr as *mut JITMem); + let jit_mem = Box::from_raw(jit_mem); + drop(jit_mem); + } + } + } +} + #[derive(Debug)] pub struct PerfEventInode { event: Box, diff --git a/kernel/src/perf/tracepoint.rs b/kernel/src/perf/tracepoint.rs index 28ec56d6..0f3ec668 100644 --- a/kernel/src/perf/tracepoint.rs +++ b/kernel/src/perf/tracepoint.rs @@ -5,6 +5,7 @@ use crate::filesystem::page_cache::PageCache; use crate::libs::casting::DowncastArc; use crate::libs::spinlock::SpinLock; use crate::perf::util::PerfProbeConfig; +use crate::perf::{BasicPerfEbpfCallBack, JITMem}; use crate::tracepoint::{TracePoint, TracePointCallBackFunc}; use crate::{ filesystem::vfs::{file::File, FilePrivateData, FileSystem, IndexNode}, @@ -16,7 +17,7 @@ use alloc::sync::Arc; use alloc::{string::String, vec::Vec}; use core::any::Any; use core::sync::atomic::AtomicUsize; -use rbpf::EbpfVmRawOwned; +use rbpf::EbpfVmRaw; use system_error::SystemError; #[derive(Debug)] @@ -74,29 +75,14 @@ impl IndexNode for TracepointPerfEvent { } } -pub struct TracePointPerfCallBack { - _bpf_prog_file: Arc, - vm: EbpfVmRawOwned, -} - -impl TracePointPerfCallBack { - fn new(bpf_prog_file: Arc, vm: EbpfVmRawOwned) -> Self { - Self { - _bpf_prog_file: bpf_prog_file, - vm, - } - } -} +pub struct TracePointPerfCallBack(BasicPerfEbpfCallBack); impl TracePointCallBackFunc for TracePointPerfCallBack { fn call(&self, entry: &[u8]) { // ebpf needs a mutable slice let entry = unsafe { core::slice::from_raw_parts_mut(entry.as_ptr() as *mut u8, entry.len()) }; - let res = self.vm.execute_program(entry); - if res.is_err() { - log::error!("tracepoint callback error: {:?}", res); - } + self.0.call(entry); } } @@ -109,15 +95,39 @@ impl PerfEventOps for TracepointPerfEvent { .downcast_arc::() .ok_or(SystemError::EINVAL)?; let prog_slice = file.insns(); - let mut vm = EbpfVmRawOwned::new(Some(prog_slice.to_vec())).map_err(|e| { + + let prog_slice = + unsafe { core::slice::from_raw_parts(prog_slice.as_ptr(), prog_slice.len()) }; + let mut vm = EbpfVmRaw::new(Some(prog_slice)).map_err(|e| { log::error!("create ebpf vm failed: {:?}", e); SystemError::EINVAL })?; - vm.register_helper_set(BPF_HELPER_FUN_SET.get()) - .map_err(|_| SystemError::EINVAL)?; + for (id, f) in BPF_HELPER_FUN_SET.get() { + vm.register_helper(*id, *f) + .map_err(|_| SystemError::EINVAL)?; + } // create a callback to execute the ebpf prog - let callback = Box::new(TracePointPerfCallBack::new(file, vm)); + let callback; + + #[cfg(target_arch = "x86_64")] + { + log::info!("Using JIT compilation for BPF program on x86_64 architecture"); + let jit_mem = Box::new(JITMem::new()); + let jit_mem = Box::leak(jit_mem); + let jit_mem_addr = core::ptr::from_ref::(jit_mem) as usize; + vm.set_jit_exec_memory(jit_mem).unwrap(); + vm.jit_compile().unwrap(); + let basic_callback = BasicPerfEbpfCallBack::new(file, vm, jit_mem_addr); + callback = Box::new(TracePointPerfCallBack(basic_callback)); + } + #[cfg(not(target_arch = "x86_64"))] + { + vm.register_allowed_memory(0..u64::MAX); + let basic_callback = BasicPerfEbpfCallBack::new(file, vm); + callback = Box::new(TracePointPerfCallBack(basic_callback)); + } + let id = CALLBACK_ID.fetch_add(1, core::sync::atomic::Ordering::Relaxed); self.tp.register_raw_callback(id, callback); diff --git a/triagebot.toml b/triagebot.toml index ffc4dfc3..166316aa 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -84,7 +84,6 @@ trigger_files = ["kernel/src/virt", "kernel/src/arch/x86_64/kvm"] [autolabel."T-Obs and Test"] trigger_files = [ - "/kernel/crates/rbpf", "/kernel/crates/kprobe", "/kernel/src/debug/kprobe", "/kernel/src/bpf", @@ -167,7 +166,6 @@ infra = ["@dragonos/infra"] "/kernel/src/arch/x86_64/kvm" = ["virtulization"] "/kernel/src/arch/x86_64" = ["x86_64"] "/kernel/src/arch/riscv64" = ["riscv64"] -"/kernel/crates/rbpf" = ["sig-obs"] "/kernel/crates/kprobe" = ["sig-obs"] "/kernel/src/debug/kprobe" = ["sig-obs"] "/kernel/src/bpf" = ["sig-obs"]