Compare commits

...

25 Commits

Author SHA1 Message Date
eea42459fd Add some documentation 2025-09-15 15:59:23 +02:00
8110528e1c Increase benchmark iterations 2025-09-15 15:52:52 +02:00
054403ae0d Also write the total cyc time 2025-09-12 13:27:08 +02:00
c2684b995b Make time_tsc refer to the entire runtime again
Also correct tsc errors by adding the cyc duration
2025-09-12 12:44:41 +02:00
737682ab16 Don't remove nyx tools if setting up the workdir failed 2025-09-12 12:16:30 +02:00
bad2725805 Change qemu compilation mode back to lto, oops 2025-09-12 11:55:28 +02:00
d7cf79a092 Add anyhow for better errors 2025-09-12 11:54:23 +02:00
9a08103872 Fix which benchmarks are compile
This prevents errors with redefinitions, the lzy way
2025-09-12 11:53:53 +02:00
f9b0bf0b5f Correct for errors in the current tsc by using cyc 2025-09-12 11:53:11 +02:00
2732383465 Adjust tsc calculation
Since we only want to record the time when benchmarking
2025-09-12 11:04:02 +02:00
9ba2bbe8cd Fix compilation of embench
The separation did not work before, somehow.
The new approach of redefining the function names, while hacky, definitely works though.
2025-09-12 10:54:24 +02:00
7666a650c2 Revert to lto linking for qemu 2025-09-10 16:15:58 +02:00
718a7fe078 Add nyx benchmark without pt 2025-09-10 16:14:30 +02:00
3e82b1610d Remove dbg! 2025-09-09 14:31:13 +02:00
f59947aea8 Increase benchmark duration for better results 2025-09-09 14:24:55 +02:00
2cc2264dde Add script to setup kvm 2025-09-08 16:35:11 +02:00
b44a0a5ef3 Execute each benchmark as individual program
- Client now produces one binary per benchmark
2025-09-08 16:35:04 +02:00
2d88a8539f Move install nyx functionality from build script to runtime
It did not make a lot of sense to have that in the buildscript, as that will make nyx_runner dependent on the target directory.
This also allows specifying a binary to trace at runtime
2025-09-07 14:36:02 +02:00
57ff2f187c Add embench
for now, compile all benchmarks (except one) and run serially
2025-09-06 17:12:01 +02:00
c2e432c219 Benchmark nyx and baseline 2025-09-05 15:00:47 +02:00
d14e0fe3a0 Calculate the bus frequency at runtime
The 100mHz value was guaranteed by intel for this CPU,
but this approach is more reliable
2025-09-04 18:32:58 +02:00
f072c0ce53 Use correct conversion for time values
Two errors I made:
* cycle durations are not constant, their value relies on the cbr
* tsc frequency was not exactly 2.7Ghz, now calculated at runtime
2025-09-04 18:11:20 +02:00
10ca8f89f6 Track total time using tsc packets and total internal time with cycles.
Something is still wrong though, the tsc time is somehow less than the cyc time
2025-09-04 16:31:17 +02:00
2e5af76dbb Track time in main and time disabled 2025-09-03 12:03:23 +02:00
109091c087 Simplify client, no need for hypercalls anymore 2025-09-03 11:47:56 +02:00
39 changed files with 998 additions and 706 deletions

3
.gitignore vendored
View File

@ -1,2 +1,5 @@
/target
*.log
/nyx_data
/KVM-Nyx-fork
/benchmarks

350
Cargo.lock generated
View File

@ -2,6 +2,21 @@
# It is not intended for manual editing.
version = 4
[[package]]
name = "addr2line"
version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
dependencies = [
"gimli",
]
[[package]]
name = "adler2"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "aho-corasick"
version = "1.1.3"
@ -61,12 +76,36 @@ dependencies = [
"windows-sys 0.60.2",
]
[[package]]
name = "anyhow"
version = "1.0.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100"
dependencies = [
"backtrace",
]
[[package]]
name = "autocfg"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "backtrace"
version = "0.3.74"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
dependencies = [
"addr2line",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
"windows-targets 0.52.6",
]
[[package]]
name = "base-x"
version = "0.2.11"
@ -81,11 +120,11 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
[[package]]
name = "bindgen"
version = "0.72.0"
version = "0.72.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f72209734318d0b619a5e0f5129918b848c416e122a3c4ce054e03cb87b726f"
checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895"
dependencies = [
"bitflags 2.9.1",
"bitflags 2.9.4",
"cexpr",
"clang-sys",
"itertools",
@ -96,7 +135,7 @@ dependencies = [
"regex",
"rustc-hash",
"shlex",
"syn 2.0.104",
"syn 2.0.106",
]
[[package]]
@ -107,9 +146,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.9.1"
version = "2.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394"
[[package]]
name = "bumpalo"
@ -130,24 +169,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eadd868a2ce9ca38de7eeafdcec9c7065ef89b42b32f0839278d55f35c54d1ff"
dependencies = [
"clap",
"heck",
"heck 0.4.1",
"indexmap",
"log",
"proc-macro2",
"quote",
"serde",
"serde_json",
"syn 2.0.104",
"syn 2.0.106",
"tempfile",
"toml",
]
[[package]]
name = "cc"
version = "1.2.31"
version = "1.2.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2"
checksum = "5252b3d2648e5eedbc1a6f501e3c795e07025c1e93bbf8bbdd6eef7f447a6d54"
dependencies = [
"find-msvc-tools",
"shlex",
]
@ -162,9 +202,9 @@ dependencies = [
[[package]]
name = "cfg-if"
version = "1.0.1"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
[[package]]
name = "clang-sys"
@ -179,18 +219,19 @@ dependencies = [
[[package]]
name = "clap"
version = "4.5.42"
version = "4.5.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed87a9d530bb41a67537289bafcac159cb3ee28460e0a4571123d2a778a6a882"
checksum = "7eac00902d9d136acd712710d71823fb8ac8004ca445a89e73a41d45aa712931"
dependencies = [
"clap_builder",
"clap_derive",
]
[[package]]
name = "clap_builder"
version = "4.5.42"
version = "4.5.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64f4f3f3c77c94aff3c7e9aac9a2ca1974a5adf392a8bb751e827d6d127ab966"
checksum = "2ad9bbf750e73b5884fb8a211a9424a1906c1e156724260fdae972f31d70e1d6"
dependencies = [
"anstream",
"anstyle",
@ -198,6 +239,18 @@ dependencies = [
"strsim",
]
[[package]]
name = "clap_derive"
version = "4.5.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c"
dependencies = [
"heck 0.5.0",
"proc-macro2",
"quote",
"syn 2.0.106",
]
[[package]]
name = "clap_lex"
version = "0.7.5"
@ -209,7 +262,6 @@ name = "client"
version = "0.1.0"
dependencies = [
"cc",
"libc",
]
[[package]]
@ -240,7 +292,7 @@ dependencies = [
[[package]]
name = "config"
version = "0.1.0"
source = "git+https://git.cs.tu-dortmund.de/david.venhoff/libnyx-fork#d85fc2487a6427a0df699c7eaee37db70bc6858b"
source = "git+https://git.cs.tu-dortmund.de/david.venhoff/libnyx-fork#b2a71111ab428fb95055239efe84f6e81f86d4b7"
dependencies = [
"libc",
"ron",
@ -254,6 +306,27 @@ version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f8a2ca5ac02d09563609681103aada9e1777d54fc57a5acd7a41404f9c93b6e"
[[package]]
name = "csv"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf"
dependencies = [
"csv-core",
"itoa",
"ryu",
"serde",
]
[[package]]
name = "csv-core"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d02f3b0da4c6504f86e9cd789d8dbafab48c2321be74e9987593de5a894d93d"
dependencies = [
"memchr",
]
[[package]]
name = "derivative"
version = "2.2.0"
@ -282,7 +355,7 @@ checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.104",
"syn 2.0.106",
]
[[package]]
@ -311,12 +384,12 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "errno"
version = "0.3.13"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys 0.60.2",
"windows-sys 0.61.0",
]
[[package]]
@ -325,6 +398,12 @@ version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]]
name = "find-msvc-tools"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fd99930f64d146689264c637b5af2f0233a933bef0d8570e2526bf9e083192d"
[[package]]
name = "fs4"
version = "0.5.4"
@ -338,7 +417,7 @@ dependencies = [
[[package]]
name = "fuzz_runner"
version = "0.1.0"
source = "git+https://git.cs.tu-dortmund.de/david.venhoff/libnyx-fork#d85fc2487a6427a0df699c7eaee37db70bc6858b"
source = "git+https://git.cs.tu-dortmund.de/david.venhoff/libnyx-fork#b2a71111ab428fb95055239efe84f6e81f86d4b7"
dependencies = [
"byteorder",
"colored",
@ -378,20 +457,26 @@ dependencies = [
"cfg-if",
"libc",
"r-efi",
"wasi 0.14.2+wasi-0.2.4",
"wasi 0.14.5+wasi-0.2.4",
]
[[package]]
name = "glob"
version = "0.3.2"
name = "gimli"
version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]]
name = "glob"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
[[package]]
name = "hashbrown"
version = "0.15.4"
version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
[[package]]
name = "heck"
@ -400,10 +485,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "indexmap"
version = "2.10.0"
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "indexmap"
version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "206a8042aec68fa4a62e8d3f7aa4ceb508177d9324faf261e1959e495b7a1921"
dependencies = [
"equivalent",
"hashbrown",
@ -438,9 +529,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
version = "0.2.174"
version = "0.2.175"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543"
[[package]]
name = "libipt"
@ -448,7 +539,7 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c3143c4dae9794d23fa2bbc6315847fdf3ef718caa09a7ba09238bce19fe9d4"
dependencies = [
"bitflags 2.9.1",
"bitflags 2.9.4",
"derive_more",
"libipt-sys",
"num_enum",
@ -477,7 +568,7 @@ dependencies = [
[[package]]
name = "libnyx"
version = "0.1.0"
source = "git+https://git.cs.tu-dortmund.de/david.venhoff/libnyx-fork#d85fc2487a6427a0df699c7eaee37db70bc6858b"
source = "git+https://git.cs.tu-dortmund.de/david.venhoff/libnyx-fork#b2a71111ab428fb95055239efe84f6e81f86d4b7"
dependencies = [
"cbindgen",
"config",
@ -487,15 +578,15 @@ dependencies = [
[[package]]
name = "linux-raw-sys"
version = "0.9.4"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
[[package]]
name = "log"
version = "0.4.27"
version = "0.4.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
[[package]]
name = "memchr"
@ -505,9 +596,9 @@ checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
[[package]]
name = "memmap2"
version = "0.9.7"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "483758ad303d734cec05e5c12b41d7e93e6a6390c5e9dae6bdeb7c1259012d28"
checksum = "843a98750cd611cc2965a8213b53b43e715f13c37a9e096c6408e69990961db7"
dependencies = [
"libc",
]
@ -527,6 +618,15 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniz_oxide"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
dependencies = [
"adler2",
]
[[package]]
name = "nix"
version = "0.26.4"
@ -569,7 +669,16 @@ dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn 2.0.104",
"syn 2.0.106",
]
[[package]]
name = "object"
version = "0.36.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
dependencies = [
"memchr",
]
[[package]]
@ -601,12 +710,12 @@ dependencies = [
[[package]]
name = "prettyplease"
version = "0.2.36"
version = "0.2.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff24dfcda44452b9816fff4cd4227e1bb73ff5a2f1bc1105aa92fb8565ce44d2"
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
dependencies = [
"proc-macro2",
"syn 2.0.104",
"syn 2.0.106",
]
[[package]]
@ -626,9 +735,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
[[package]]
name = "proc-macro2"
version = "1.0.95"
version = "1.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
dependencies = [
"unicode-ident",
]
@ -639,12 +748,16 @@ version = "0.1.0"
dependencies = [
"libipt",
"memmap2",
"raw-cpuid",
]
[[package]]
name = "qemu-nyx-runner"
version = "0.1.0"
dependencies = [
"anyhow",
"clap",
"csv",
"libnyx",
"pt-dump-decoder",
]
@ -712,10 +825,19 @@ dependencies = [
]
[[package]]
name = "regex"
version = "1.11.1"
name = "raw-cpuid"
version = "11.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186"
dependencies = [
"bitflags 2.9.4",
]
[[package]]
name = "regex"
version = "1.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912"
dependencies = [
"aho-corasick",
"memchr",
@ -725,9 +847,9 @@ dependencies = [
[[package]]
name = "regex-automata"
version = "0.4.9"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6"
dependencies = [
"aho-corasick",
"memchr",
@ -736,9 +858,9 @@ dependencies = [
[[package]]
name = "regex-syntax"
version = "0.8.5"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001"
[[package]]
name = "ron"
@ -751,6 +873,12 @@ dependencies = [
"serde",
]
[[package]]
name = "rustc-demangle"
version = "0.1.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace"
[[package]]
name = "rustc-hash"
version = "2.1.1"
@ -768,22 +896,22 @@ dependencies = [
[[package]]
name = "rustix"
version = "1.0.8"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8"
checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e"
dependencies = [
"bitflags 2.9.1",
"bitflags 2.9.4",
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.60.2",
"windows-sys 0.61.0",
]
[[package]]
name = "rustversion"
version = "1.0.21"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "ryu"
@ -823,14 +951,14 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.104",
"syn 2.0.106",
]
[[package]]
name = "serde_json"
version = "1.0.142"
version = "1.0.143"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7"
checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a"
dependencies = [
"itoa",
"memchr",
@ -976,9 +1104,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.104"
version = "2.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
dependencies = [
"proc-macro2",
"quote",
@ -987,15 +1115,15 @@ dependencies = [
[[package]]
name = "tempfile"
version = "3.20.0"
version = "3.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1"
checksum = "84fa4d11fadde498443cca10fd3ac23c951f0dc59e080e9f4b93d4df4e4eea53"
dependencies = [
"fastrand",
"getrandom 0.3.3",
"once_cell",
"rustix",
"windows-sys 0.59.0",
"windows-sys 0.61.0",
]
[[package]]
@ -1088,9 +1216,9 @@ checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
[[package]]
name = "unicode-ident"
version = "1.0.18"
version = "1.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d"
[[package]]
name = "utf8parse"
@ -1112,44 +1240,54 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]]
name = "wasi"
version = "0.14.2+wasi-0.2.4"
version = "0.14.5+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
checksum = "a4494f6290a82f5fe584817a676a34b9d6763e8d9d18204009fb31dceca98fd4"
dependencies = [
"wit-bindgen-rt",
"wasip2",
]
[[package]]
name = "wasip2"
version = "1.0.0+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03fa2761397e5bd52002cd7e73110c71af2109aca4e521a9f40473fe685b0a24"
dependencies = [
"wit-bindgen",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.100"
version = "0.2.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
checksum = "7e14915cadd45b529bb8d1f343c4ed0ac1de926144b746e2710f9cd05df6603b"
dependencies = [
"cfg-if",
"once_cell",
"rustversion",
"wasm-bindgen-macro",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.100"
version = "0.2.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
checksum = "e28d1ba982ca7923fd01448d5c30c6864d0a14109560296a162f80f305fb93bb"
dependencies = [
"bumpalo",
"log",
"proc-macro2",
"quote",
"syn 2.0.104",
"syn 2.0.106",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.100"
version = "0.2.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
checksum = "7c3d463ae3eff775b0c45df9da45d68837702ac35af998361e2c84e7c5ec1b0d"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@ -1157,22 +1295,22 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.100"
version = "0.2.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
checksum = "7bb4ce89b08211f923caf51d527662b75bdc9c9c7aab40f86dcb9fb85ac552aa"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.104",
"syn 2.0.106",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.100"
version = "0.2.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
checksum = "f143854a3b13752c6950862c906306adb27c7e839f7414cec8fea35beab624c1"
dependencies = [
"unicode-ident",
]
@ -1205,6 +1343,12 @@ version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
[[package]]
name = "windows-link"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65"
[[package]]
name = "windows-sys"
version = "0.59.0"
@ -1223,6 +1367,15 @@ dependencies = [
"windows-targets 0.53.3",
]
[[package]]
name = "windows-sys"
version = "0.61.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e201184e40b2ede64bc2ea34968b28e33622acdbbf37104f0e4a33f7abe657aa"
dependencies = [
"windows-link 0.2.0",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
@ -1245,7 +1398,7 @@ version = "0.53.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91"
dependencies = [
"windows-link",
"windows-link 0.1.3",
"windows_aarch64_gnullvm 0.53.0",
"windows_aarch64_msvc 0.53.0",
"windows_i686_gnu 0.53.0",
@ -1354,38 +1507,35 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
[[package]]
name = "winnow"
version = "0.7.12"
version = "0.7.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95"
checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf"
dependencies = [
"memchr",
]
[[package]]
name = "wit-bindgen-rt"
version = "0.39.0"
name = "wit-bindgen"
version = "0.45.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
dependencies = [
"bitflags 2.9.1",
]
checksum = "5c573471f125075647d03df72e026074b7203790d41351cd6edc96f46bcccd36"
[[package]]
name = "zerocopy"
version = "0.8.26"
version = "0.8.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f"
checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.26"
version = "0.8.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.104",
"syn 2.0.106",
]

View File

@ -9,4 +9,10 @@ edition = "2024"
[dependencies]
libnyx = { git = "https://git.cs.tu-dortmund.de/david.venhoff/libnyx-fork" }
pt-dump-decoder = { path = "pt-dump-decoder" }
pt-dump-decoder = { path = "pt-dump-decoder" }
csv = "1.3.1"
clap.workspace = true
anyhow = { version = "1.0.99", features = ["backtrace"] }
[workspace.dependencies]
clap = { version = "4.5.47", features = ["derive"] }

12
README.md Normal file
View File

@ -0,0 +1,12 @@
# qemu-nyx-runner
This repository contains the source code of `qemu-nyx-runner`,
a tool which can be used to execute programs inside a `Nyx` hypervisor, trace them with `Intel PT`,
and analyze the resulting trace files to estimate its execution without all this instrumentation.
## Set Up
To use this project, `KVM-Nyx-fork`s kernel modules have to be installed and loaded.
use the `setup-kvm.sh` script for that.
To run a program with `qemu-nyx-runner`, use `cargo r --release -- path/to/program`

18
benchmark_all.sh Executable file
View File

@ -0,0 +1,18 @@
set -e
cargo build --workspace --release
echo "Disabling turbo..."
echo 1 | sudo tee /sys/devices/system/cpu/intel_pstate/no_turbo
for file in client/src/bin/*.rs; do
bin_name=$(basename "$file" .rs)
bin_path=./target/release/${bin_name}
echo "Benchmarking $bin_name..."
sudo -E nice -n -20 taskset -c 0 ./target/release/qemu-nyx-runner "$bin_path"
done
echo 0 | sudo tee /sys/devices/system/cpu/intel_pstate/no_turbo
echo "Done!"

101
build.rs
View File

@ -1,101 +0,0 @@
use std::env;
use std::path::Path;
use std::process::Command;
fn main() {
println!("cargo:rerun-if-changed=client");
let out_dir = env::var("OUT_DIR").unwrap();
let client_bin = build_client(&out_dir);
setup_nyx(&out_dir);
create_nyx_workdir(&out_dir, &client_bin);
}
#[track_caller]
fn shell(cwd: impl AsRef<Path>, command_string: &str) {
println!(
"cargo:warning=Running ({}) {command_string}",
cwd.as_ref().display()
);
let cwd = cwd.as_ref();
let mut parts = command_string.split(" ");
let command_name = parts.next().unwrap();
let mut command = Command::new(command_name);
command.current_dir(cwd);
command.stdout(std::process::Stdio::inherit());
command.stderr(std::process::Stdio::inherit());
command.args(parts);
let mut process = command.spawn().unwrap();
let exit_status = process.wait().unwrap();
if !exit_status.success() {
panic!(
"Command failed with {exit_status}: {}> {command_string}",
cwd.display()
);
}
}
/// Builds the client and returns the path to its binary
fn build_client(out_dir: &str) -> String {
// Change the target dir to avoid a cargo target dir deadlock
shell(
"client",
&format!("cargo build --release --target-dir {out_dir}/client_target"),
);
format!("{out_dir}/client_target/release/client")
}
/// Downloads and compiles qemu-nyx and packer and compiles them for the current architecture
/// This means that cross-compilation is not supported
fn setup_nyx(out_dir: &str) {
let packer_path = std::path::PathBuf::from(format!("{out_dir}/packer"));
let qemu_path = std::path::PathBuf::from(format!("{out_dir}/QEMU-Nyx"));
if !packer_path.exists() {
shell(out_dir, "git clone https://github.com/nyx-fuzz/packer/");
}
if !qemu_path.exists() {
println!("cargo:warning=Cloning and building QEMU-Nyx. This may take a while...");
shell(
out_dir,
"git clone https://github.com/nyx-fuzz/QEMU-Nyx --depth 1",
);
let is_debug_build = match env::var("DEBUG").unwrap().as_str() {
"true" => true,
"false" => false,
other => panic!("Invalid value for DEBUG: {other}"),
};
let qemu_compile_mode = if is_debug_build {
"debug_static"
} else {
"lto"
};
shell(
qemu_path,
&format!("bash compile_qemu_nyx.sh {qemu_compile_mode}"),
);
}
}
fn create_nyx_workdir(out_dir: &str, client_bin: &str) {
// Create the directory and move required binaries to it
shell(
out_dir,
&format!(
"python3 packer/packer/nyx_packer.py {client_bin} build afl processor_trace --fast_reload_mode --purge"
),
);
// Create the nyx config
shell(
out_dir,
"python3 packer/packer/nyx_config_gen.py build Kernel",
);
// Pass the path to the build directory to src/main.rs
println!("cargo:rustc-env=NYX_SHAREDIR={out_dir}/build")
}

View File

@ -3,8 +3,5 @@ name = "client"
version = "0.1.0"
edition = "2024"
[dependencies]
libc = "*"
[build-dependencies]
cc = "1.0"
cc = "1.2.36"

View File

@ -1,5 +1,104 @@
fn main() {
println!("cargo:rerun-if-changed=nyx-wrapper.c");
//! Somewhat hacky build script for downloading and linking embench.
//! Each benchmark is compiled individually, so that we can run them through rust
//! and add some instrumentation around the benchmarks.
cc::Build::new().file("nyx-wrapper.c").compile("nyx");
use std::fmt::Write;
use std::path::PathBuf;
use std::process::Command;
use std::str::FromStr;
use std::{env, fs};
const BENCHMARKS: &[&str] = &[
"src/ud/libud.c",
"src/wikisort/libwikisort.c",
"src/nbody/nbody.c",
"src/edn/libedn.c",
// "src/st/libst.c", Collides with matmult-int
"src/aha-mont64/mont64.c",
"src/matmult-int/matmult-int.c",
"src/slre/libslre.c",
"src/tarfind/tarfind.c",
"src/sglib-combined/combined.c",
"src/huffbench/libhuffbench.c",
"src/nsichneu/libnsichneu.c",
"src/minver/libminver.c",
"src/statemate/libstatemate.c",
"src/crc32/crc_32.c",
// "src/picojpeg/libpicojpeg.c", Contains multiple source fails, maybe a todo if relevant
"src/cubic/basicmath_small.c",
"src/md5sum/md5.c",
"src/qrduino/qrtest.c",
"src/nettle-aes/nettle-aes.c",
"src/primecount/primecount.c",
"src/nettle-sha256/nettle-sha256.c",
];
fn main() {
println!("cargo:rerun-if-changed=build.rs");
let out_dir = env::var("OUT_DIR").unwrap();
Command::new("git")
.args(["clone", "https://github.com/embench/embench-iot"])
.current_dir(&out_dir)
.spawn()
.unwrap()
.wait()
.unwrap();
let embench_dir = PathBuf::from_str(&format!("{out_dir}/embench-iot")).unwrap();
let mut generated_code = String::new();
for &benchmark_file in BENCHMARKS {
let path = embench_dir.join(benchmark_file);
let dir = path.parent().unwrap();
let benchmark_name = dir.file_name().unwrap().to_string_lossy().replace("-", "_");
let mut build = cc::Build::new();
build
.include(embench_dir.join("support"))
.file(embench_dir.join("support/beebsc.c"))
.file(&path)
.warnings(false)
// Just use some roughly accurate value, it just influences how long each benchmark runs
.define("CPU_MHZ", "2700");
// Rename all functions that are defined multiple times to avoid collisions
let colliding_functions = [
"benchmark",
"initialise_benchmark",
"warm_caches",
"verify_benchmark",
];
for name in colliding_functions {
build.define(name, format!("{benchmark_name}_{name}").as_str());
}
build.compile(&format!("libembench_{benchmark_name}.a"));
write!(
generated_code,
r#"
#[allow(non_camel_case_types)] pub struct {benchmark_name};
unsafe extern "C" {{
fn {benchmark_name}_benchmark();
fn {benchmark_name}_initialise_benchmark();
}}
impl Benchmark for {benchmark_name} {{
unsafe fn init() {{
unsafe {{ {benchmark_name}_initialise_benchmark(); }}
}}
unsafe fn benchmark() {{
unsafe {{ {benchmark_name}_benchmark(); }}
}}
}}
"#
)
.unwrap();
}
fs::write(format!("{out_dir}/libembench-sys.rs"), generated_code).unwrap();
}

View File

@ -1,9 +0,0 @@
#include "nyx.h"
void rust_hprintf(const char * format) {
hprintf(format);
}
uint32_t rust_perform_hypercall(uint32_t hypercall, uint32_t argument) {
return kAFL_hypercall(hypercall, argument);
}

View File

@ -1,372 +0,0 @@
/*
This file is part of NYX.
Copyright (c) 2021 Sergej Schumilo
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.
*/
#ifndef KAFL_USER_H
#define KAFL_USER_H
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#ifndef __MINGW64__
#include <sys/mman.h>
#endif
#ifdef __MINGW64__
#ifndef uint64_t
#define uint64_t UINT64
#endif
#ifndef int32_t
#define int32_t INT32
#endif
#ifndef uint8_t
#define uint8_t UINT8
#endif
#else
#include <stdint.h>
#endif
#define HYPERCALL_KAFL_RAX_ID 0x01f
#define HYPERCALL_KAFL_ACQUIRE 0
#define HYPERCALL_KAFL_GET_PAYLOAD 1
/* deprecated */
#define HYPERCALL_KAFL_GET_PROGRAM 2
/* deprecated */
#define HYPERCALL_KAFL_GET_ARGV 3
#define HYPERCALL_KAFL_RELEASE 4
#define HYPERCALL_KAFL_SUBMIT_CR3 5
#define HYPERCALL_KAFL_SUBMIT_PANIC 6
/* deprecated */
#define HYPERCALL_KAFL_SUBMIT_KASAN 7
#define HYPERCALL_KAFL_PANIC 8
/* deprecated */
#define HYPERCALL_KAFL_KASAN 9
#define HYPERCALL_KAFL_LOCK 10
/* deprecated */
#define HYPERCALL_KAFL_INFO 11
#define HYPERCALL_KAFL_NEXT_PAYLOAD 12
#define HYPERCALL_KAFL_PRINTF 13
/* deprecated */
#define HYPERCALL_KAFL_PRINTK_ADDR 14
/* deprecated */
#define HYPERCALL_KAFL_PRINTK 15
/* user space only hypercalls */
#define HYPERCALL_KAFL_USER_RANGE_ADVISE 16
#define HYPERCALL_KAFL_USER_SUBMIT_MODE 17
#define HYPERCALL_KAFL_USER_FAST_ACQUIRE 18
/* 19 is already used for exit reason KVM_EXIT_KAFL_TOPA_MAIN_FULL */
#define HYPERCALL_KAFL_USER_ABORT 20
#define HYPERCALL_KAFL_RANGE_SUBMIT 29
#define HYPERCALL_KAFL_REQ_STREAM_DATA 30
#define HYPERCALL_KAFL_PANIC_EXTENDED 32
#define HYPERCALL_KAFL_CREATE_TMP_SNAPSHOT 33
#define HYPERCALL_KAFL_DEBUG_TMP_SNAPSHOT 34 /* hypercall for debugging / development purposes */
#define HYPERCALL_KAFL_GET_HOST_CONFIG 35
#define HYPERCALL_KAFL_SET_AGENT_CONFIG 36
#define HYPERCALL_KAFL_DUMP_FILE 37
#define HYPERCALL_KAFL_REQ_STREAM_DATA_BULK 38
#define HYPERCALL_KAFL_PERSIST_PAGE_PAST_SNAPSHOT 39
/* hypertrash only hypercalls */
#define HYPERTRASH_HYPERCALL_MASK 0xAA000000
#define HYPERCALL_KAFL_NESTED_PREPARE (0 | HYPERTRASH_HYPERCALL_MASK)
#define HYPERCALL_KAFL_NESTED_CONFIG (1 | HYPERTRASH_HYPERCALL_MASK)
#define HYPERCALL_KAFL_NESTED_ACQUIRE (2 | HYPERTRASH_HYPERCALL_MASK)
#define HYPERCALL_KAFL_NESTED_RELEASE (3 | HYPERTRASH_HYPERCALL_MASK)
#define HYPERCALL_KAFL_NESTED_HPRINTF (4 | HYPERTRASH_HYPERCALL_MASK)gre
#define HPRINTF_MAX_SIZE 0x1000 /* up to 4KB hprintf strings */
/* specific defines to enable support for NYX hypercalls on unmodified KVM builds */
/* PIO port number used by VMWare backdoor */
#define VMWARE_PORT 0x5658
/* slightly changed RAX_ID to avoid vmware backdoor collisions */
#define HYPERCALL_KAFL_RAX_ID_VMWARE 0x8080801f
typedef struct{
int32_t size;
uint8_t data[];
} kAFL_payload;
typedef struct{
uint64_t ip[4];
uint64_t size[4];
uint8_t enabled[4];
} kAFL_ranges;
#define KAFL_MODE_64 0
#define KAFL_MODE_32 1
#define KAFL_MODE_16 2
#if defined(__i386__)
#define KAFL_HYPERCALL_NO_PT(_ebx, _ecx) ({ \
uint32_t _eax = HYPERCALL_KAFL_RAX_ID_VMWARE; \
do{ \
asm volatile( \
"outl %%eax, %%dx;" \
: "+a" (_eax) \
: "b" (_ebx), "c" (_ecx), "d" (VMWARE_PORT) \
: "cc", "memory" \
); \
} while(0); \
_eax; \
})
#define KAFL_HYPERCALL_PT(_ebx, _ecx) ({ \
uint32_t _eax = HYPERCALL_KAFL_RAX_ID; \
do{ \
asm volatile( \
"vmcall;" \
: "+a" (_eax) \
: "b" (_ebx), "c" (_ecx) \
: "cc", "memory" \
); \
} while(0); \
_eax; \
})
#else
#define KAFL_HYPERCALL_NO_PT(_rbx, _rcx) ({ \
uint64_t _rax = HYPERCALL_KAFL_RAX_ID_VMWARE; \
do{ \
asm volatile( \
"outl %%eax, %%dx;" \
: "+a" (_rax) \
: "b" (_rbx), "c" (_rcx), "d" (VMWARE_PORT) \
: "cc", "memory" \
); \
} while(0); \
_rax; \
})
#define KAFL_HYPERCALL_PT(_rbx, _rcx) ({ \
uint64_t _rax = HYPERCALL_KAFL_RAX_ID; \
do{ \
asm volatile( \
"vmcall;" \
: "+a" (_rax) \
: "b" (_rbx), "c" (_rcx) \
: "cc", "memory" \
); \
} while(0); \
_rax; \
})
#endif
#if defined(__i386__)
#ifdef NO_PT_NYX
#define KAFL_HYPERCALL(__rbx, __rcx) \
KAFL_HYPERCALL_NO_PT(_rbx, _rcx); \
}while(0)
static inline uint32_t kAFL_hypercall(uint32_t rbx, uint32_t rcx){
return KAFL_HYPERCALL_NO_PT(rbx, rcx);
}
#else
#define KAFL_HYPERCALL(__rbx, __rcx) \
KAFL_HYPERCALL_PT(_rbx, _rcx); \
}while(0)
static inline uint32_t kAFL_hypercall(uint32_t rbx, uint32_t rcx){
# ifndef __NOKAFL
return KAFL_HYPERCALL_PT(rbx, rcx);
# endif
return 0;
}
#endif
#elif defined(__x86_64__)
#ifdef NO_PT_NYX
#define KAFL_HYPERCALL(__rbx, __rcx) \
KAFL_HYPERCALL_NO_PT(_rbx, _rcx); \
}while(0)
static inline uint64_t kAFL_hypercall(uint64_t rbx, uint64_t rcx){
return KAFL_HYPERCALL_NO_PT(rbx, rcx);
}
#else
#define KAFL_HYPERCALL(__rbx, __rcx) \
KAFL_HYPERCALL_PT(_rbx, _rcx); \
}while(0)
static inline uint64_t kAFL_hypercall(uint64_t rbx, uint64_t rcx){
# ifndef __NOKAFL
return KAFL_HYPERCALL_PT(rbx, rcx);
# endif
return 0;
}
#endif
#endif
//extern uint8_t* hprintf_buffer;
static inline uint8_t alloc_hprintf_buffer(uint8_t** hprintf_buffer){
if(!*hprintf_buffer){
#ifdef __MINGW64__
*hprintf_buffer = (uint8_t*)VirtualAlloc(0, HPRINTF_MAX_SIZE, MEM_COMMIT, PAGE_READWRITE);
#else
*hprintf_buffer = (uint8_t*)mmap((void*)NULL, HPRINTF_MAX_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
#endif
if(!*hprintf_buffer){
return 0;
}
}
return 1;
}
#ifdef __NOKAFL
int (*hprintf)(const char * format, ...) = printf;
#else
static void hprintf(const char * format, ...) __attribute__ ((unused));
static void hprintf(const char * format, ...){
static uint8_t* hprintf_buffer = NULL;
va_list args;
va_start(args, format);
if(alloc_hprintf_buffer(&hprintf_buffer)){
vsnprintf((char*)hprintf_buffer, HPRINTF_MAX_SIZE, format, args);
kAFL_hypercall(HYPERCALL_KAFL_PRINTF, (uintptr_t)hprintf_buffer);
}
//vprintf(format, args);
va_end(args);
}
#endif
static void habort(char* msg){
kAFL_hypercall(HYPERCALL_KAFL_USER_ABORT, (uintptr_t)msg);
}
#define NYX_HOST_MAGIC 0x4878794e
#define NYX_AGENT_MAGIC 0x4178794e
#define NYX_HOST_VERSION 2
#define NYX_AGENT_VERSION 1
typedef struct host_config_s{
uint32_t host_magic;
uint32_t host_version;
uint32_t bitmap_size;
uint32_t ijon_bitmap_size;
uint32_t payload_buffer_size;
uint32_t worker_id;
/* more to come */
} __attribute__((packed)) host_config_t;
typedef struct agent_config_s{
uint32_t agent_magic;
uint32_t agent_version;
uint8_t agent_timeout_detection;
uint8_t agent_tracing;
uint8_t agent_ijon_tracing;
uint8_t agent_non_reload_mode;
uint64_t trace_buffer_vaddr;
uint64_t ijon_trace_buffer_vaddr;
uint32_t coverage_bitmap_size;
uint32_t input_buffer_size; // TODO: remove this later
uint8_t dump_payloads; /* set by hypervisor */
/* more to come */
} __attribute__((packed)) agent_config_t;
typedef struct kafl_dump_file_s{
uint64_t file_name_str_ptr;
uint64_t data_ptr;
uint64_t bytes;
uint8_t append;
} __attribute__((packed)) kafl_dump_file_t;
enum nyx_cpu_type{
unkown = 0,
nyx_cpu_v1, /* Nyx CPU used by KVM-PT */
nyx_cpu_v2 /* Nyx CPU used by vanilla KVM + VMWare backdoor */
};
#define cpuid(in,a,b,c,d)\
asm("cpuid": "=a" (a), "=b" (b), "=c" (c), "=d" (d) : "a" (in));
static int is_nyx_vcpu(void){
unsigned long eax,ebx,ecx,edx;
char str[8];
cpuid(0x80000004,eax,ebx,ecx,edx);
for(int j=0;j<4;j++){
str[j] = eax >> (8*j);
str[j+4] = ebx >> (8*j);
}
return !memcmp(&str, "NYX vCPU", 8);
}
static int get_nyx_cpu_type(void){
unsigned long eax,ebx,ecx,edx;
char str[9];
cpuid(0x80000004,eax,ebx,ecx,edx);
for(int j=0;j<4;j++){
str[j] = eax >> (8*j);
str[j+4] = ebx >> (8*j);
}
if(memcmp(&str, "NYX vCPU", 8) != 0){
return unkown;
}
for(int j=0;j<4;j++){
str[j] = ecx >> (8*j);
str[j+4] = edx >> (8*j);
}
if(memcmp(&str, " (NO-PT)", 8) != 0){
return nyx_cpu_v1;
}
return nyx_cpu_v2;
str[8] = 0;
printf("ECX: %s\n", str);
}
typedef struct req_data_bulk_s{
char file_name[256];
uint64_t num_addresses;
uint64_t addresses[479];
} req_data_bulk_t;
#endif

View File

@ -0,0 +1,5 @@
use client::program;
fn main() {
program::<client::embench_sys::sglib_combined>();
}

View File

@ -0,0 +1,5 @@
use client::program;
fn main() {
program::<client::embench_sys::crc32>();
}

View File

@ -0,0 +1,5 @@
use client::program;
fn main() {
program::<client::embench_sys::edn>();
}

View File

@ -0,0 +1,5 @@
use client::program;
fn main() {
program::<client::embench_sys::huffbench>();
}

View File

@ -0,0 +1,5 @@
use client::program;
fn main() {
program::<client::embench_sys::matmult_int>();
}

View File

@ -0,0 +1,5 @@
use client::program;
fn main() {
program::<client::embench_sys::md5sum>();
}

View File

@ -0,0 +1,5 @@
use client::program;
fn main() {
program::<client::embench_sys::minver>();
}

View File

@ -0,0 +1,5 @@
use client::program;
fn main() {
program::<client::embench_sys::aha_mont64>();
}

View File

@ -0,0 +1,5 @@
use client::program;
fn main() {
program::<client::embench_sys::nbody>();
}

View File

@ -0,0 +1,5 @@
use client::program;
fn main() {
program::<client::embench_sys::nettle_aes>();
}

View File

@ -0,0 +1,5 @@
use client::program;
fn main() {
program::<client::embench_sys::nettle_sha256>();
}

View File

@ -0,0 +1,5 @@
use client::program;
fn main() {
program::<client::embench_sys::nsichneu>();
}

View File

@ -0,0 +1,5 @@
use client::program;
fn main() {
program::<client::embench_sys::primecount>();
}

View File

@ -0,0 +1,5 @@
use client::program;
fn main() {
program::<client::embench_sys::slre>();
}

View File

@ -0,0 +1,5 @@
use client::program;
fn main() {
program::<client::embench_sys::statemate>();
}

View File

@ -0,0 +1,5 @@
use client::program;
fn main() {
program::<client::embench_sys::tarfind>();
}

View File

@ -0,0 +1,5 @@
use client::program;
fn main() {
program::<client::embench_sys::ud>();
}

View File

@ -0,0 +1,5 @@
use client::program;
fn main() {
program::<client::embench_sys::wikisort>();
}

32
client/src/lib.rs Normal file
View File

@ -0,0 +1,32 @@
use std::arch::asm;
pub mod embench_sys {
pub trait Benchmark {
unsafe fn init();
unsafe fn benchmark();
}
include!(concat!(env!("OUT_DIR"), "/libembench-sys.rs"));
}
pub use embench_sys::Benchmark;
fn ptwrite(val: u32) {
unsafe {
asm!("ptwrite {0:e}", in(reg) val);
}
}
pub fn program<B: Benchmark>() {
unsafe {
B::init();
}
let start = std::time::Instant::now();
ptwrite(42);
unsafe {
B::benchmark();
}
ptwrite(43);
println!("{}", start.elapsed().as_secs_f64());
}

View File

@ -1,55 +0,0 @@
use std::arch::asm;
use std::ffi::{CStr, CString};
unsafe extern "C" {
fn rust_hprintf(format: *const libc::c_char);
fn rust_perform_hypercall(hypercall: u32, argument: u32);
}
fn hprint(message: &CStr) {
unsafe {
rust_hprintf(message.as_ptr());
}
}
enum NyxHypercall {
Acquire = 0,
// Release = 4,
// Panic = 8,
}
fn hypercall(hypercall: NyxHypercall, argument: u32) {
unsafe {
rust_perform_hypercall(hypercall as u32, argument);
}
}
fn ptwrite(val: u32) {
unsafe {
asm!("ptwrite {0:e}", in(reg) val);
}
}
fn main() {
hprint(c"Acuiring again!\n");
hypercall(NyxHypercall::Acquire, 100);
let start = std::time::Instant::now();
let mut last_time = std::time::Duration::ZERO;
ptwrite(42);
loop {
let elapsed = start.elapsed();
if elapsed.as_secs() >= 2 {
let msg = CString::new(format!("Passed: {:?}\n", elapsed.as_secs())).unwrap();
let msg: &'static CStr = Box::leak(Box::new(msg));
hprint(msg);
break;
}
if last_time.as_secs() != elapsed.as_secs() {
hprint(c"A second passed\n");
}
last_time = elapsed;
}
ptwrite(43);
hprint(c"Done\n");
}

View File

@ -9,3 +9,4 @@ edition = "2024"
[dependencies]
libipt = { version = "0.4.0", features = ["libipt_master"] }
memmap2 = "0.9.7"
raw-cpuid = "11.5.0"

View File

@ -1,6 +1,8 @@
use libipt::enc_dec_builder::EncoderDecoderBuilder;
use libipt::packet::{Packet, PacketDecoder};
use memmap2::Mmap;
use raw_cpuid::{CpuId, cpuid};
use std::collections::HashMap;
use std::error::Error;
use std::fs::File;
use std::path::Path;
@ -9,70 +11,130 @@ use std::time::Duration;
#[derive(Debug)]
pub struct AnalyzeData {
pub packets: u64,
pub cycles: u64,
pub time: Duration,
pub time_outside_hypervisor: Duration,
pub lost_cyc: u64,
pub time_qemu: Duration,
pub time_tsc: Duration,
pub time_cyc: Duration,
pub time_main: Duration,
}
#[derive(Debug)]
#[derive(Debug, Eq, PartialEq)]
enum Scope {
Main,
PreRun,
PostRun,
}
pub fn analyze_dump(path: impl AsRef<Path>) -> Result<AnalyzeData, Box<dyn Error>> {
pub fn analyze_dump(
path: impl AsRef<Path>,
qemu_duration: Duration,
) -> Result<AnalyzeData, Box<dyn Error>> {
let trace_file = File::open(path)?;
// Safety: We only read from the trace file, never write to it
// UB occurs when it is modified out-of-process, though
let mmap = unsafe { Mmap::map(&trace_file)? };
let builder = EncoderDecoderBuilder::<PacketDecoder<()>>::new();
// I hope this is safe if the buffer is never written to
// Safety: The mmap outlives the builder
let builder = unsafe { builder.buffer_from_raw(mmap.as_ptr() as *mut _, mmap.len()) };
let mut decoder = builder.build()?;
// Required before it can be used
decoder.sync_forward()?;
let mut segments = vec![(Scope::PreRun, 0u64)];
let mut segments = vec![(Scope::PreRun, Duration::ZERO)];
let mut total = 0u64;
let mut lost_cyc = 0;
// cpuid(0x16):ecx returns the bus frequency in mHz (See volume 2, chapter 1.3 CPUID in the intel manual)
// Combined with the core to bus ration we can calculate the core frequency
let f_bus = cpuid!(0x16).ecx as f64 * 1_000_000.0;
let mut current_cbr = 0u8;
let mut cyc_duration_before_first_tsc = Duration::ZERO;
let mut first_tsc = None::<u64>;
let mut cyc_duration_after_last_tsc = Duration::ZERO;
let mut last_tsc = None::<u64>;
let mut seen_cbrs = HashMap::<u8, u64>::new();
for packet in decoder {
total += 1;
match packet {
Ok(Packet::Cyc(cyc)) => {
segments.last_mut().unwrap().1 += cyc.value();
// Ignore cycle packets when there was no cbr packet yet.
if current_cbr == 0 {
lost_cyc += 1;
continue;
}
let f_core = f_bus * current_cbr as f64;
let duration = Duration::from_secs_f64(cyc.value() as f64 / f_core);
if first_tsc.is_none() {
cyc_duration_before_first_tsc += duration;
}
if last_tsc.is_some() {
cyc_duration_after_last_tsc += duration;
}
segments.last_mut().unwrap().1 += duration;
}
Ok(Packet::Vmcs(_)) => {
// last_packet_vmcs = true;
Ok(Packet::Tsc(tsc)) => {
if first_tsc.is_none() {
first_tsc = Some(tsc.tsc());
}
cyc_duration_after_last_tsc = Duration::ZERO;
last_tsc = Some(tsc.tsc());
}
Ok(Packet::Cbr(cbr)) => {
current_cbr = cbr.ratio();
*seen_cbrs.entry(cbr.ratio()).or_default() += 1;
}
Ok(Packet::Ptw(ptwrite)) => {
if ptwrite.payload() == 42 {
// Main enter
segments.push((Scope::Main, 0));
segments.push((Scope::Main, Duration::ZERO));
} else if ptwrite.payload() == 43 {
// Main exit
segments.push((Scope::PostRun, 0));
segments.push((Scope::PostRun, Duration::ZERO));
} else {
println!("GOT PTWRITE!!!!!: {ptwrite:?}");
// println!("GOT PTWRITE!!!!!: {ptwrite:?}");
}
}
Ok(Packet::Ovf(overflow)) => {
panic!("Got overflow packet: {overflow:?}, index {total}");
}
Ok(_) => {}
Err(error) => println!("Got error: {error:?}"),
}
}
// dbg!(&segments);
let cycles_main: u64 = segments
.iter()
.filter(|(scope, _)| matches!(scope, Scope::Main))
.map(|(_, cycles)| cycles)
.sum();
let cycles_total = segments.iter().map(|(_, cycles)| cycles).sum();
let total_seconds = cycles_total as f64 / 2_700_000_000.0;
let total_seconds_no_hypervisor = cycles_main as f64 / 2_700_000_000.0;
// dbg!(seen_cbrs);
let duration_total = segments.iter().map(|(_, duration)| *duration).sum();
let main_duration = duration_in_mode(&segments, Scope::Main);
let f_tsc = CpuId::new()
.get_tsc_info()
.unwrap()
.tsc_frequency()
.unwrap();
let first_tsc_main = first_tsc.expect("Should have recorded a tsc") as f64 / f_tsc as f64
+ cyc_duration_before_first_tsc.as_secs_f64();
let last_tsc_main = last_tsc.expect("Should have recorded a tsc") as f64 / f_tsc as f64
+ cyc_duration_after_last_tsc.as_secs_f64();
let total_seconds_tsc = last_tsc_main - first_tsc_main;
Ok(AnalyzeData {
packets: total,
cycles: cycles_total,
time: Duration::from_secs_f64(total_seconds),
time_outside_hypervisor: Duration::from_secs_f64(total_seconds_no_hypervisor),
lost_cyc,
time_cyc: duration_total,
time_main: main_duration,
time_tsc: Duration::from_secs_f64(total_seconds_tsc),
time_qemu: qemu_duration,
})
}
fn duration_in_mode(segments: &[(Scope, Duration)], scope: Scope) -> Duration {
segments
.iter()
.filter(|(it, _)| *it == scope)
.map(|(_, duration)| *duration)
.sum::<Duration>()
}

10
setup-kvm.sh Executable file
View File

@ -0,0 +1,10 @@
if [ ! -d KVM-Nyx-fork ]; then
echo "Could not find KVM-Nyx-fork"
echo "Download from https://git.cs.tu-dortmund.de/david.venhoff/KVM-Nyx-fork"
echo "Then setup the config with \`make oldconfig\`"
exit
fi
cd KVM-Nyx-fork || exit
sh compile_kvm_nyx_standalone.sh
sh load_kvm_nyx.sh
echo "Done"

17
src/benchmark.rs Normal file
View File

@ -0,0 +1,17 @@
use std::fmt::Debug;
use std::io::Write;
/// Used for benchmarking nyx + pt, nyx - pt, and the baseline
pub trait Benchmark {
const TITLE: &'static str;
type BenchmarkResult: ToCsv + Debug;
fn execute_once(&mut self) -> Self::BenchmarkResult;
}
pub trait ToCsv {
const HEADERS: &'static [&'static str];
fn write(&self, writer: &mut csv::Writer<impl Write>) -> csv::Result<()>;
}

34
src/benchmark_baseline.rs Normal file
View File

@ -0,0 +1,34 @@
use crate::benchmark::{Benchmark, ToCsv};
use csv::Writer;
use std::io::Write;
use std::path::Path;
use std::process::Command;
use std::time::Duration;
/// Executes the given program without any instrumentation
pub struct Baseline<'a>(pub &'a Path);
impl Benchmark for Baseline<'_> {
const TITLE: &'static str = "baseline";
type BenchmarkResult = Duration;
fn execute_once(&mut self) -> Self::BenchmarkResult {
let output = Command::new(self.0)
.output()
.expect("Command should succeed");
let output = String::from_utf8_lossy(&output.stdout);
let Ok(duration) = output.trim().parse() else {
panic!("Invalid output: {}", output);
};
Duration::from_secs_f64(duration)
}
}
impl ToCsv for Duration {
const HEADERS: &'static [&'static str] = &["time"];
fn write(&self, writer: &mut Writer<impl Write>) -> csv::Result<()> {
writer.write_record([self.as_secs_f64().to_string()])
}
}

View File

@ -0,0 +1,26 @@
use crate::benchmark::Benchmark;
use crate::nyx::{NyxRunner, TraceMode};
use std::path::Path;
use std::time::Duration;
/// Executes the given program with nyx, but without tracing
pub struct BenchmarkNyxNoPt(NyxRunner);
impl BenchmarkNyxNoPt {
pub fn new(client_path: &Path) -> anyhow::Result<Self> {
Ok(BenchmarkNyxNoPt(NyxRunner::setup(
client_path,
TraceMode::Disabled,
)?))
}
}
impl Benchmark for BenchmarkNyxNoPt {
const TITLE: &'static str = "nyx";
type BenchmarkResult = Duration;
fn execute_once(&mut self) -> Self::BenchmarkResult {
self.0.execute()
}
}

43
src/benchmark_nyx_pt.rs Normal file
View File

@ -0,0 +1,43 @@
use crate::benchmark::{Benchmark, ToCsv};
use crate::nyx::{NyxRunner, TraceMode};
use csv::Writer;
use pt_dump_decoder::{AnalyzeData, analyze_dump};
use std::io::Write;
use std::path::Path;
/// Executes the given program with nyx and tracing
pub struct BenchmarkNyx(NyxRunner);
impl BenchmarkNyx {
pub fn new(client_bin: &Path) -> anyhow::Result<Self> {
Ok(BenchmarkNyx(NyxRunner::setup(
client_bin,
TraceMode::Enabled,
)?))
}
}
impl Benchmark for BenchmarkNyx {
const TITLE: &'static str = "nyx_and_pt";
type BenchmarkResult = AnalyzeData;
fn execute_once(&mut self) -> AnalyzeData {
let qemu_duration = self.0.execute();
let dump_path = self.0.workdir().join("pt_trace_dump_0");
analyze_dump(dump_path, qemu_duration).expect("Should be able to analyze the data")
}
}
impl ToCsv for AnalyzeData {
const HEADERS: &'static [&'static str] = &["time_main_cyc", "time_total_cyc", "time_total_tsc", "time_total_qemu"];
fn write(&self, writer: &mut Writer<impl Write>) -> csv::Result<()> {
writer.write_record([
self.time_main.as_secs_f64().to_string(),
self.time_cyc.as_secs_f64().to_string(),
self.time_tsc.as_secs_f64().to_string(),
self.time_qemu.as_secs_f64().to_string(),
])
}
}

View File

@ -1,47 +1,105 @@
//! Translated from libnyx/test.c
use libnyx::ffi::nyx_print_aux_buffer;
use libnyx::{NyxConfig, NyxProcess, NyxProcessRole, NyxReturnValue};
use pt_dump_decoder::analyze_dump;
use std::error::Error;
mod benchmark;
mod benchmark_baseline;
mod benchmark_nyx_no_pt;
mod benchmark_nyx_pt;
mod nyx;
const WORKDIR_PATH: &str = "/tmp/wdir";
// The sharedir path gets set by the build script
const SHAREDIR_PATH: &str = env!("NYX_SHAREDIR");
use crate::benchmark::{Benchmark, ToCsv};
use crate::benchmark_baseline::Baseline;
use crate::benchmark_nyx_no_pt::BenchmarkNyxNoPt;
use crate::benchmark_nyx_pt::BenchmarkNyx;
use anyhow::Context;
use clap::Parser;
use std::fmt::Debug;
use std::fs;
use std::path::{Path, PathBuf};
use std::time::{Duration, Instant};
fn main() -> Result<(), Box<dyn Error>> {
let _ = std::fs::remove_dir_all(WORKDIR_PATH);
run_client()?;
println!("Analyzing pt data...");
#[derive(Debug, Parser)]
#[command(about = "Tool to execute a program with nyx and trace it with intel pt")]
struct Args {
/// Path to the client binary to execute
client: PathBuf,
/// Directory where the benchmark files get stored
#[arg(short = 'o', long = "output", default_value = "benchmarks")]
output_dir: PathBuf,
/// Whether to only execute a single nyx run, used for testing
#[arg(short = 'd', long = "debug", default_value = "false")]
debug: bool,
}
fn main() -> anyhow::Result<()> {
let args = Args::parse();
if args.debug {
dbg!(BenchmarkNyx::new(&args.client)?.execute_once());
return Ok(());
}
let binary_name = args.client.file_name().unwrap().to_str().unwrap();
{
let benchmark_nyx = BenchmarkNyx::new(&args.client)?;
benchmark(binary_name, &args.output_dir, benchmark_nyx)?;
}
{
let benchmark_nyx_no_pt = BenchmarkNyxNoPt::new(&args.client)?;
benchmark(binary_name, &args.output_dir, benchmark_nyx_no_pt)?;
}
benchmark(binary_name, &args.output_dir, Baseline(&args.client))?;
let dump_path = format!("{WORKDIR_PATH}/pt_trace_dump_0");
let result = analyze_dump(dump_path)?;
println!("{result:?}");
Ok(())
}
fn run_client() -> Result<(), Box<dyn Error>> {
let mut nyx_config = NyxConfig::load(SHAREDIR_PATH)?;
nyx_config.set_workdir_path(WORKDIR_PATH.to_string());
nyx_config.set_input_buffer_size(0x2000);
nyx_config.set_nyx_debug_log_path("qemu_nyx.log".to_string());
fn benchmark<B: Benchmark>(
binary_name: &str,
output_dir: &Path,
mut benchmark: B,
) -> anyhow::Result<()> {
warmup(&mut benchmark);
let results = benchmark_loop(&mut benchmark);
write_results::<B>(binary_name, output_dir, &results).with_context(|| "Writing results")?;
Ok(())
}
/// Warm up to make sure caches are initialized, etc.
fn warmup(benchmark: &mut impl Benchmark) {
let warmup_duration = Duration::from_secs(5);
println!("Warming up for {warmup_duration:?}");
let mut iterations = 0;
let start = Instant::now();
while start.elapsed() < warmup_duration {
benchmark.execute_once();
iterations += 1;
}
println!("Warmed up for {iterations} iterations");
}
fn benchmark_loop<B: Benchmark>(benchmark: &mut B) -> Vec<B::BenchmarkResult> {
let n = 500;
println!("Perform {n} iterations...");
(0..n)
.map(|_| std::hint::black_box(benchmark.execute_once()))
.collect::<Vec<_>>()
}
fn write_results<B: Benchmark>(
binary_name: &str,
output_dir: &Path,
results: &[B::BenchmarkResult],
) -> anyhow::Result<()> {
fs::create_dir_all(output_dir)
.with_context(|| format!("Creating output directory {output_dir:?}"))?;
let filename = format!("benchmark_{binary_name}_{}", B::TITLE);
let file =
fs::File::create(output_dir.join(filename)).with_context(|| "Creating output file")?;
let mut writer = csv::WriterBuilder::new().from_writer(file);
writer.write_record(B::BenchmarkResult::HEADERS)?;
for result in results {
result.write(&mut writer)?;
}
nyx_config.set_process_role(NyxProcessRole::StandAlone);
nyx_config.print();
let mut nyx_runner = NyxProcess::new(&mut nyx_config, 0)?;
nyx_runner.option_set_trace_mode(true);
nyx_runner.option_set_timeout(10, 0);
nyx_runner.option_apply();
let result = nyx_runner.exec();
assert!(
matches!(result, NyxReturnValue::Normal),
"Unexpected return value {result:?}"
);
nyx_print_aux_buffer(&mut nyx_runner as *mut _);
nyx_runner.shutdown();
Ok(())
}

171
src/nyx.rs Normal file
View File

@ -0,0 +1,171 @@
//! This module contains functionality to download and setup nyx,
//! so that it can be used to execute a binary.
use anyhow::{Context, anyhow, bail};
use libnyx::{NyxConfig, NyxProcess, NyxProcessRole, NyxReturnValue};
use std::path::{Path, PathBuf};
use std::process::Command;
use std::time::Duration;
use std::{fs, io};
const WORKDIR_PATH: &str = "/tmp/wdir";
#[derive(Debug, Clone, Copy)]
pub enum TraceMode {
Enabled,
Disabled,
}
pub struct NyxRunner(NyxProcess);
impl NyxRunner {
pub fn setup(client_bin: &Path, trace_mode: TraceMode) -> anyhow::Result<Self> {
if let Err(e) = fs::remove_dir_all(WORKDIR_PATH)
&& !matches!(e.kind(), io::ErrorKind::NotFound)
{
panic!("Could not remove working directory at {WORKDIR_PATH}");
}
let sharedir = setup_nyx(&PathBuf::from("nyx_data"), client_bin)?;
let sharedir = sharedir.to_str().expect("Expected unicode path");
let mut nyx_config = NyxConfig::load(sharedir)
.map_err(|err| anyhow!(err))
.with_context(|| "Creating nyx config")?;
nyx_config.set_workdir_path(WORKDIR_PATH.to_string());
nyx_config.set_input_buffer_size(0x2000);
nyx_config.set_nyx_debug_log_path("qemu_nyx.log".to_string());
nyx_config.set_pt_enabled(matches!(trace_mode, TraceMode::Enabled));
nyx_config.set_process_role(NyxProcessRole::StandAlone);
nyx_config.print();
let mut nyx_runner = NyxProcess::new(&mut nyx_config, 0)
.map_err(|err| anyhow!(err))
.with_context(|| "Creating Nyx Process")?;
nyx_runner.option_set_trace_mode(true);
nyx_runner.option_set_reload_mode(false);
nyx_runner.option_set_timeout(10, 0);
nyx_runner.option_apply();
Ok(NyxRunner(nyx_runner))
}
/// Executes the program with Nyx and intel PT.
///
/// Returns the duration of the program as recorded by QEMU.
pub fn execute(&mut self) -> Duration {
let result = self.0.exec();
// nyx_print_aux_buffer(&mut self.0 as *mut _);
assert!(
matches!(result, NyxReturnValue::Normal),
"Unexpected return value: {result:?}"
);
self.0.aux_result_duration()
}
pub fn workdir(&self) -> &Path {
Path::new(WORKDIR_PATH)
}
}
impl Drop for NyxRunner {
fn drop(&mut self) {
self.0.shutdown();
}
}
/// Downloads and installs nyx
///
/// [out_dir] Is the directory to which all tools get downloaded and
/// where the working directory gets created.
///
/// [client] Is the path to the client binary.
///
/// Returns the path to the working directory
pub fn setup_nyx(out_dir: &Path, client: &Path) -> anyhow::Result<PathBuf> {
if !out_dir.exists() {
fs::create_dir(out_dir)?;
}
println!("Installing nyx...");
let result = setup_nyx_tools(out_dir);
if let Err(e) = result {
// Remove the out directory as it may be tainted
fs::remove_dir_all(out_dir)
.with_context(|| format!("Error while handling error: {e}"))?;
return Err(e);
}
create_nyx_workdir(out_dir, client).with_context(|| "Creating the nyx working directory")
}
#[track_caller]
fn shell(cwd: impl AsRef<Path>, command_string: &str) -> anyhow::Result<()> {
println!("Running ({}) {command_string}", cwd.as_ref().display());
let cwd = cwd.as_ref();
let mut parts = command_string.split(" ");
let command_name = parts.next().unwrap();
let mut command = Command::new(command_name);
command.current_dir(cwd);
command.stdout(std::process::Stdio::inherit());
command.stderr(std::process::Stdio::inherit());
command.args(parts);
let mut process = command.spawn()?;
let exit_status = process.wait()?;
if !exit_status.success() {
bail!("Command ({command_string}) failed with {exit_status}");
}
Ok(())
}
/// Downloads and compiles qemu-nyx and packer and compiles them for the current architecture
/// This means that cross-compilation is not supported
fn setup_nyx_tools(out_dir: &Path) -> anyhow::Result<()> {
let packer_path = out_dir.join("packer");
let qemu_path = out_dir.join("QEMU-Nyx");
if !packer_path.exists() {
shell(out_dir, "git clone https://github.com/nyx-fuzz/packer/")?;
}
if !qemu_path.exists() {
println!("Cloning and building QEMU-Nyx. This may take a while...");
shell(
out_dir,
"git clone https://git.cs.tu-dortmund.de/david.venhoff/QEMU-Nyx-fork QEMU-Nyx --depth 1",
)?;
// Compile with maximum optimizations
let qemu_compile_mode = "lto";
shell(
qemu_path,
&format!("bash compile_qemu_nyx.sh {qemu_compile_mode}"),
)?;
}
Ok(())
}
fn create_nyx_workdir(out_dir: &Path, client_bin: &Path) -> anyhow::Result<PathBuf> {
let client_bin =
fs::canonicalize(client_bin).with_context(|| format!("canonicalizing {client_bin:?}"))?;
let client_bin = client_bin.to_str().expect("Expected unicode path");
// Create the directory and move required binaries to it
shell(
out_dir,
&format!(
"python3 packer/packer/nyx_packer.py {client_bin} build afl processor_trace --purge"
),
)?;
// Create the nyx config
shell(
out_dir,
"python3 packer/packer/nyx_config_gen.py build Kernel",
)?;
Ok(out_dir.join("build"))
}