diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 93dd70c885..7287fa1421 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -280,6 +280,7 @@ jobs: # Forkserver - ./fuzzers/forkserver/forkserver_simple + - ./fuzzers/forkserver/forkserver_capture_stdout - ./fuzzers/forkserver/forkserver_libafl_cc - ./fuzzers/forkserver/fuzzbench_forkserver - ./fuzzers/forkserver/fuzzbench_forkserver_cmplog diff --git a/fuzzers/baby/backtrace_baby_fuzzers/command_executor/src/main.rs b/fuzzers/baby/backtrace_baby_fuzzers/command_executor/src/main.rs index b7c37ffa55..cea20832b7 100644 --- a/fuzzers/baby/backtrace_baby_fuzzers/command_executor/src/main.rs +++ b/fuzzers/baby/backtrace_baby_fuzzers/command_executor/src/main.rs @@ -117,8 +117,11 @@ pub fn main() { } let timeout = Duration::from_secs(5); - let mut executor = - MyExecutor { shmem_id, timeout }.into_executor(tuple_list!(observer, bt_observer)); + let mut executor = MyExecutor { shmem_id, timeout }.into_executor( + tuple_list!(observer, bt_observer), + None, + None, + ); // Generator of printable bytearrays of max size 32 let mut generator = RandPrintablesGenerator::new(nonzero!(32)); diff --git a/fuzzers/baby/backtrace_baby_fuzzers/forkserver_executor/src/main.rs b/fuzzers/baby/backtrace_baby_fuzzers/forkserver_executor/src/main.rs index 7fe90178f9..666b7f513e 100644 --- a/fuzzers/baby/backtrace_baby_fuzzers/forkserver_executor/src/main.rs +++ b/fuzzers/baby/backtrace_baby_fuzzers/forkserver_executor/src/main.rs @@ -25,7 +25,7 @@ use libafl_bolts::{ rands::StdRand, shmem::{ShMem, ShMemProvider}, tuples::tuple_list, - AsSliceMut, TargetArgs, + AsSliceMut, StdTargetArgs, }; pub fn main() { diff --git a/fuzzers/binary_only/intel_pt_command_executor/src/main.rs b/fuzzers/binary_only/intel_pt_command_executor/src/main.rs index 5c4b177fd6..24fbc25931 100644 --- a/fuzzers/binary_only/intel_pt_command_executor/src/main.rs +++ b/fuzzers/binary_only/intel_pt_command_executor/src/main.rs @@ -136,6 +136,8 @@ pub fn main() { command_configurator, tuple_list!(observer), tuple_list!(hook), + None, + None, ); // Generator of printable bytearrays of max size 32 diff --git a/fuzzers/forkserver/forkserver_capture_stdout/.gitignore b/fuzzers/forkserver/forkserver_capture_stdout/.gitignore new file mode 100644 index 0000000000..6774267cad --- /dev/null +++ b/fuzzers/forkserver/forkserver_capture_stdout/.gitignore @@ -0,0 +1 @@ +forkserver_capture_stdout \ No newline at end of file diff --git a/fuzzers/forkserver/forkserver_capture_stdout/Cargo.lock b/fuzzers/forkserver/forkserver_capture_stdout/Cargo.lock new file mode 100644 index 0000000000..ac32e6850a --- /dev/null +++ b/fuzzers/forkserver/forkserver_capture_stdout/Cargo.lock @@ -0,0 +1,1290 @@ +# This file is automatically @generated by Cargo. +# 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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" +dependencies = [ + "anstyle", + "once_cell", + "windows-sys", +] + +[[package]] +name = "arbitrary-int" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "825297538d77367557b912770ca3083f778a196054b3ee63b22673c4a3cae0a5" + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[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 = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitbybit" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb157f9753a7cddfcf4a4f5fed928fbf4ce1b7b64b6bcc121d7a9f95d698997b" +dependencies = [ + "arbitrary-int", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "bitflags" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" + +[[package]] +name = "bytemuck" +version = "1.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "clap" +version = "4.5.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e958897981290da2a852763fe9cdb89cd36977a5d729023127095fa94d95e2ff" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83b0f35019843db2160b5bb19ae09b4e6411ac33fc6a712003c33e03090e2489" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" + +[[package]] +name = "cobs" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + +[[package]] +name = "const_format" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "const_panic" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2459fc9262a1aa204eb4b5764ad4f189caec88aea9634389c0a25f8be7f6265e" + +[[package]] +name = "ctor" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e9666f4a9a948d4f1dff0c08a4512b0f7c86414b23960104c243c10d79f4c3" +dependencies = [ + "ctor-proc-macro", + "dtor", +] + +[[package]] +name = "ctor-proc-macro" +version = "0.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f211af61d8efdd104f96e57adf5e426ba1bc3ed7a4ead616e15e5881fd79c4d" + +[[package]] +name = "dtor" +version = "0.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "222ef136a1c687d4aa0395c175f2c4586e379924c352fd02f7870cf7de783c23" +dependencies = [ + "dtor-proc-macro", +] + +[[package]] +name = "dtor-proc-macro" +version = "0.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7454e41ff9012c00d53cf7f475c5e3afa3b91b7c90568495495e8d9bf47a1055" + +[[package]] +name = "embedded-io" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + +[[package]] +name = "env_filter" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3716d7a920fb4fac5d84e9d4bce8ceb321e9414b4409da61b07b75c1e3d0697" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "jiff", + "log", +] + +[[package]] +name = "erased-serde" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e004d887f51fcb9fef17317a2f3525c887d8aa3f4f50fed920816a688284a5b7" +dependencies = [ + "serde", + "typeid", +] + +[[package]] +name = "fastbloom" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27cea6e7f512d43b098939ff4d5a5d6fe3db07971e1d05176fe26c642d33f5b8" +dependencies = [ + "getrandom", + "siphasher", + "wide 0.7.32 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "forkserver_capture_stdout" +version = "0.14.1" +dependencies = [ + "clap", + "env_logger", + "libafl", + "libafl_bolts", + "log", + "nix", + "serde", + "serde_json", +] + +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "getrandom" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "serde", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hostname" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9c7c7c8ac16c798734b8a24560c1362120597c40d5e1459f09498f8f6c8f2ba" +dependencies = [ + "cfg-if", + "libc", + "windows 0.52.0", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "jiff" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c102670231191d07d37a35af3eb77f1f0dbf7a71be51a962dcd57ea607be7260" +dependencies = [ + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde", +] + +[[package]] +name = "jiff-static" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cdde31a9d349f1b1f51a0b3714a5940ac022976f4b49485fc04be052b183b4c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "libafl" +version = "0.15.2" +dependencies = [ + "ahash", + "arbitrary-int", + "backtrace", + "bincode", + "bitbybit", + "const_format", + "const_panic", + "fastbloom", + "fs2", + "hashbrown", + "libafl_bolts", + "libafl_derive", + "libc", + "libm", + "log", + "meminterval", + "nix", + "num-traits", + "postcard", + "regex", + "rustversion", + "serde", + "serde_json", + "serial_test", + "tuple_list", + "typed-builder", + "uuid", + "wait-timeout", + "winapi", + "windows 0.59.0", +] + +[[package]] +name = "libafl_bolts" +version = "0.15.2" +dependencies = [ + "ahash", + "backtrace", + "ctor", + "erased-serde", + "hashbrown", + "hostname", + "libafl_derive", + "libc", + "log", + "mach2", + "miniz_oxide", + "nix", + "num_enum", + "once_cell", + "postcard", + "rand_core", + "rustversion", + "serde", + "serial_test", + "static_assertions", + "tuple_list", + "typeid", + "uds", + "uuid", + "wide 0.7.32 (git+https://github.com/Lokathor/wide?rev=71b5df0b2620da753836fafce5f99076181a49fe)", + "winapi", + "windows 0.59.0", + "windows-result", + "xxhash-rust", +] + +[[package]] +name = "libafl_derive" +version = "0.15.2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "libc" +version = "0.2.171" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" + +[[package]] +name = "libm" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "mach2" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" +dependencies = [ + "libc", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "meminterval" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f8614cf855d251be1c2138d330c04f134923fddec0dcfc8b6f58ac499bf248" +dependencies = [ + "num-traits", + "serde", +] + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "miniz_oxide" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" +dependencies = [ + "adler2", +] + +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", + "memoffset", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_enum" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc" + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "portable-atomic" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" + +[[package]] +name = "portable-atomic-util" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "postcard" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "170a2601f67cc9dba8edd8c4870b15f71a6a2dc196daec8c83f72b59dff628a8" +dependencies = [ + "cobs", + "embedded-io 0.4.0", + "embedded-io 0.6.1", + "serde", +] + +[[package]] +name = "proc-macro2" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" + +[[package]] +name = "redox_syscall" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustversion" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "safe_arch" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b02de82ddbe1b636e6170c21be622223aea188ef2e139be0a5b219ec215323" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "scc" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea091f6cac2595aa38993f04f4ee692ed43757035c36e67c180b6828356385b1" +dependencies = [ + "sdd", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sdd" +version = "3.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "584e070911c7017da6cb2eb0788d09f43d789029b5877d3e5ecc8acf86ceee21" + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serial_test" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b258109f244e1d6891bf1053a55d63a5cd4f8f4c30cf9a1280989f80e7a1fa9" +dependencies = [ + "log", + "once_cell", + "parking_lot", + "scc", + "serial_test_derive", +] + +[[package]] +name = "serial_test_derive" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d69265a08751de7844521fd15003ae0a888e035773ba05695c5c759a6f89eef" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + +[[package]] +name = "smallvec" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tuple_list" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "141fb9f71ee586d956d7d6e4d5a9ef8e946061188520140f7591b668841d502e" + +[[package]] +name = "typed-builder" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce63bcaf7e9806c206f7d7b9c1f38e0dce8bb165a80af0898161058b19248534" +dependencies = [ + "typed-builder-macro", +] + +[[package]] +name = "typed-builder-macro" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60d8d828da2a3d759d3519cdf29a5bac49c77d039ad36d0782edadbf9cd5415b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + +[[package]] +name = "uds" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "885c31f06fce836457fe3ef09a59f83fe8db95d270b11cd78f40a4666c4d1661" +dependencies = [ + "libc", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "uuid" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" +dependencies = [ + "getrandom", + "serde", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wait-timeout" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" +dependencies = [ + "libc", +] + +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wide" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41b5576b9a81633f3e8df296ce0063042a73507636cbe956c61133dd7034ab22" +dependencies = [ + "bytemuck", + "safe_arch", +] + +[[package]] +name = "wide" +version = "0.7.32" +source = "git+https://github.com/Lokathor/wide?rev=71b5df0b2620da753836fafce5f99076181a49fe#71b5df0b2620da753836fafce5f99076181a49fe" +dependencies = [ + "bytemuck", + "safe_arch", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core 0.52.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f919aee0a93304be7f62e8e5027811bbba96bcb1de84d6618be56e43f8a32a1" +dependencies = [ + "windows-core 0.59.0", + "windows-targets 0.53.0", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "810ce18ed2112484b0d4e15d022e5f598113e220c53e373fb31e67e21670c1ce" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result", + "windows-strings", + "windows-targets 0.53.0", +] + +[[package]] +name = "windows-implement" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83577b051e2f49a058c308f17f273b570a6a758386fc291b5f6a934dd84e48c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" + +[[package]] +name = "windows-result" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags", +] + +[[package]] +name = "xxhash-rust" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/fuzzers/forkserver/forkserver_capture_stdout/Cargo.toml b/fuzzers/forkserver/forkserver_capture_stdout/Cargo.toml new file mode 100644 index 0000000000..a9435975aa --- /dev/null +++ b/fuzzers/forkserver/forkserver_capture_stdout/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "forkserver_capture_stdout" +version = "0.14.1" +authors = ["tokatoka ", "Ziqiao Kong "] +edition = "2024" + +[profile.dev] +panic = "abort" + +[profile.release] +panic = "abort" +lto = true +codegen-units = 1 +opt-level = 3 + +[dependencies] +clap = { version = "4.5.18", features = ["derive"] } +env_logger = "0.11.5" +libafl = { path = "../../../libafl", features = ["std", "derive"] } +libafl_bolts = { path = "../../../libafl_bolts" } +log = { version = "0.4.22", features = ["release_max_level_info"] } +nix = { version = "0.29.0", features = ["signal"] } +serde = "1.0.219" +serde_json = "1.0.140" diff --git a/fuzzers/forkserver/forkserver_capture_stdout/README.md b/fuzzers/forkserver/forkserver_capture_stdout/README.md new file mode 100644 index 0000000000..efe0e2112b --- /dev/null +++ b/fuzzers/forkserver/forkserver_capture_stdout/README.md @@ -0,0 +1,14 @@ +# Simple Forkserver Fuzzer to Capture Stdout + +This is a simple example fuzzer to showcase how to capture stdout(stderr) from `ForkserverExecutor`. + +## Usage +You can build this example by `cargo build --release`. +This downloads AFLplusplus/AFLplusplus and compiles the example harness program in src/program.c with afl-cc + +## Run +After you build it you can run +`cp ./target/release/forkserver_capture_stdout .` to copy the fuzzer into this directory, +and you can run +`taskset -c 1 ./forkserver_capture_stdout ./target/release/program ./corpus/ -t 1000` to run the fuzzer. +`taskset` binds this process to a specific core to improve the throughput. \ No newline at end of file diff --git a/fuzzers/forkserver/forkserver_capture_stdout/build.rs b/fuzzers/forkserver/forkserver_capture_stdout/build.rs new file mode 100644 index 0000000000..bda99090a8 --- /dev/null +++ b/fuzzers/forkserver/forkserver_capture_stdout/build.rs @@ -0,0 +1,61 @@ +use std::{ + env, + path::Path, + process::{Command, exit}, +}; + +const AFL_URL: &str = "https://github.com/AFLplusplus/AFLplusplus"; + +fn main() { + if cfg!(windows) { + println!("cargo:warning=No support for windows yet."); + exit(0); + } + + unsafe { + env::remove_var("DEBUG"); + } + let cwd = env::current_dir().unwrap().to_string_lossy().to_string(); + + let afl = format!("{}/AFLplusplus", &cwd); + let afl_cc = format!("{}/AFLplusplus/afl-cc", &cwd); + + let afl_path = Path::new(&afl); + let afl_cc_path = Path::new(&afl_cc); + + if !afl_path.is_dir() { + println!("cargo:warning=AFL++ not found, downloading..."); + Command::new("git") + .arg("clone") + .arg(AFL_URL) + .status() + .unwrap(); + } + + if !afl_cc_path.is_file() { + let mut afl_cc_make = Command::new("make"); + afl_cc_make.arg("all").current_dir(afl_path); + if let Ok(llvm_config) = env::var("LLVM_CONFIG") { + if !llvm_config.is_empty() { + afl_cc_make.env("LLVM_CONFIG", llvm_config); + } + } + afl_cc_make.status().unwrap(); + } + + let mut compile_command = Command::new(afl_cc_path); + compile_command + .args(["src/program.c", "-o"]) + .arg(format!("{cwd}/target/release/program")); + + if let Ok(llvm_config) = env::var("LLVM_CONFIG") { + if !llvm_config.is_empty() { + compile_command.env("LLVM_CONFIG", llvm_config); + } + } + + compile_command.status().unwrap(); + + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=src/"); +} diff --git a/fuzzers/forkserver/forkserver_capture_stdout/src/main.rs b/fuzzers/forkserver/forkserver_capture_stdout/src/main.rs new file mode 100644 index 0000000000..acc89af2d4 --- /dev/null +++ b/fuzzers/forkserver/forkserver_capture_stdout/src/main.rs @@ -0,0 +1,261 @@ +use core::time::Duration; +use std::{ops::Index, path::PathBuf}; + +use clap::Parser; +use libafl::{ + HasMetadata, + corpus::{Corpus, InMemoryCorpus, OnDiskCorpus}, + events::SimpleEventManager, + executors::{ + CommandExecutor, DiffExecutor, HasObservers, StdChildArgs, forkserver::ForkserverExecutor, + }, + feedback_and_fast, feedback_or, + feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback}, + fuzzer::{Fuzzer, StdFuzzer}, + inputs::BytesInput, + monitors::SimpleMonitor, + mutators::{HavocScheduledMutator, Tokens, havoc_mutations, tokens_mutations}, + observers::{CanTrack, HitcountsMapObserver, StdMapObserver, StdOutObserver, TimeObserver}, + schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler}, + stages::mutational::StdMutationalStage, + state::{HasCorpus, StdState}, +}; +use libafl_bolts::{ + AsSliceMut, StdTargetArgs, Truncate, current_nanos, + rands::StdRand, + shmem::{ShMem, ShMemProvider, UnixShMemProvider}, + tuples::{Handled, Merge, tuple_list}, +}; +use nix::sys::signal::Signal; +use serde::Deserialize; + +/// The commandline args this fuzzer accepts +#[derive(Debug, Parser)] +#[command( + name = "forkserver_simple", + about = "This is a simple example fuzzer to fuzz a executable instrumented by afl-cc.", + author = "tokatoka " +)] +struct Opt { + #[arg( + help = "The instrumented binary we want to fuzz", + name = "EXEC", + required = true + )] + executable: String, + + #[arg( + help = "The directory to read initial inputs from ('seeds')", + name = "INPUT_DIR", + required = true + )] + in_dir: PathBuf, + + #[arg( + help = "Timeout for each individual execution, in milliseconds", + short = 't', + long = "timeout", + default_value = "1200" + )] + timeout: u64, + + #[arg( + help = "If not set, the child's stdout and stderror will be redirected to /dev/null", + short = 'd', + long = "debug-child", + default_value = "false" + )] + debug_child: bool, + + #[arg( + help = "Arguments passed to the target", + name = "arguments", + num_args(1..), + allow_hyphen_values = true, + )] + arguments: Vec, + + #[arg( + help = "Signal used to stop child", + short = 's', + long = "signal", + value_parser = str::parse::, + default_value = "SIGKILL" + )] + signal: Signal, +} + +#[derive(Debug, Clone, Deserialize)] +struct ProgramOutput { + len: i32, +} + +pub fn main() { + env_logger::init(); + const MAP_SIZE: usize = 65536; + + let opt = Opt::parse(); + + let corpus_dirs: Vec = [opt.in_dir].to_vec(); + + // The unix shmem provider supported by AFL++ for shared memory + let mut shmem_provider = UnixShMemProvider::new().unwrap(); + + // The coverage map shared between observer and executor + let mut shmem = shmem_provider.new_shmem(MAP_SIZE).unwrap(); + // let the forkserver know the shmid + unsafe { + shmem.write_to_env("__AFL_SHM_ID").unwrap(); + } + let shmem_buf = shmem.as_slice_mut(); + + // Create an observation channel using the signals map + let edges_observer = unsafe { + HitcountsMapObserver::new(StdMapObserver::new("shared_mem", shmem_buf)).track_indices() + }; + + // Create an observation channel to keep track of the execution time + let time_observer = TimeObserver::new("time"); + + // Feedback to rate the interestingness of an input + // This one is composed by two Feedbacks in OR + let mut feedback = feedback_or!( + // New maximization map feedback linked to the edges observer and the feedback state + MaxMapFeedback::new(&edges_observer), + // Time feedback, this one does not need a feedback state + TimeFeedback::new(&time_observer) + ); + + // A feedback to choose if an input is a solution or not + // We want to do the same crash deduplication that AFL does + let mut objective = feedback_and_fast!( + // Must be a crash + CrashFeedback::new(), + // Take it only if trigger new coverage over crashes + // Uses `with_name` to create a different history from the `MaxMapFeedback` in `feedback` above + MaxMapFeedback::with_name("mapfeedback_metadata_objective", &edges_observer) + ); + + // create a State from scratch + let mut state = StdState::new( + // RNG + StdRand::with_seed(current_nanos()), + // Corpus that will be evolved, we keep it in memory for performance + InMemoryCorpus::::new(), + // Corpus in which we store solutions (crashes in this example), + // on disk so the user can get them after stopping the fuzzer + OnDiskCorpus::new(PathBuf::from("./crashes")).unwrap(), + // States of the feedbacks. + // The feedbacks can report the data that should persist in the State. + &mut feedback, + // Same for objective feedbacks + &mut objective, + ) + .unwrap(); + + // The Monitor trait define how the fuzzer stats are reported to the user + let monitor = SimpleMonitor::new(|s| println!("{s}")); + + // The event manager handle the various events generated during the fuzzing loop + // such as the notification of the addition of a new item to the corpus + let mut mgr = SimpleEventManager::new(monitor); + + // A minimization+queue policy to get testcasess from the corpus + let scheduler = IndexesLenTimeMinimizerScheduler::new(&edges_observer, QueueScheduler::new()); + + // A fuzzer with feedbacks and a corpus scheduler + let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); + + // If we should debug the child + let debug_child = opt.debug_child; + + // Create the executor for the forkserver + let args = opt.arguments; + + let observer_ref = edges_observer.handle(); + let stdout = StdOutObserver::new("stdout".into()).expect("observer"); + let stdout_handle = stdout.handle(); + + let cmd_stdout = StdOutObserver::new("cmd_stdout".into()).expect("observer"); + let cmd_stdout_handle = cmd_stdout.handle(); + + let mut tokens = Tokens::new(); + let mut executor = ForkserverExecutor::builder() + .program(opt.executable.clone()) + .debug_child(debug_child) + .shmem_provider(&mut shmem_provider) + .autotokens(&mut tokens) + .parse_afl_cmdline(args.clone()) + .coverage_map_size(MAP_SIZE) + .timeout(Duration::from_millis(opt.timeout)) + .kill_signal(opt.signal) + .stdout_observer(stdout_handle.clone()) + .build(tuple_list!(time_observer, edges_observer, stdout)) + .unwrap(); + + let cmd_executor = CommandExecutor::builder() + .program(opt.executable) + .debug_child(debug_child) + .parse_afl_cmdline(args) + .timeout(Duration::from_millis(opt.timeout)) + .stdout_observer(cmd_stdout_handle.clone()) + .build(tuple_list!(cmd_stdout)) + .unwrap(); + + if let Some(dynamic_map_size) = executor.coverage_map_size() { + executor.observers_mut()[&observer_ref] + .as_mut() + .truncate(dynamic_map_size); + } + + let mut executor = DiffExecutor::new(executor, cmd_executor, ()); + // In case the corpus is empty (on first run), reset + if state.must_load_initial_inputs() { + state + .load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &corpus_dirs) + .unwrap_or_else(|err| { + panic!( + "Failed to load initial corpus at {:?}: {:?}", + &corpus_dirs, err + ) + }); + println!("We imported {} inputs from disk.", state.corpus().count()); + } + + state.add_metadata(tokens); + + // Setup a mutational stage with a basic bytes mutator + let mutator = + HavocScheduledMutator::with_max_stack_pow(havoc_mutations().merge(tokens_mutations()), 6); + let mut stages = tuple_list!(StdMutationalStage::new(mutator)); + + for _ in 0..5 { + fuzzer + .fuzz_one(&mut stages, &mut executor, &mut state, &mut mgr) + .unwrap(); + + let stdout = executor + .observers() + .index(&stdout_handle) + .output + .clone() + .expect("no stdout"); + let out: ProgramOutput = serde_json::from_slice(&stdout).unwrap(); + println!( + "Program output from Forkserver after serde_json::from_slice is {:?}", + &out + ); + + let cmd_stdout = executor + .observers() + .index(&cmd_stdout_handle) + .output + .clone() + .expect("no stdout"); + let out: ProgramOutput = serde_json::from_slice(&stdout).unwrap(); + println!( + "Program output from CommandExecutor after serde_json::from_slice is {:?}", + &out + ); + } +} diff --git a/fuzzers/forkserver/forkserver_capture_stdout/src/program.c b/fuzzers/forkserver/forkserver_capture_stdout/src/program.c new file mode 100644 index 0000000000..233af3c4d9 --- /dev/null +++ b/fuzzers/forkserver/forkserver_capture_stdout/src/program.c @@ -0,0 +1,24 @@ +#include + +// The following line is needed for shared memeory testcase fuzzing +__AFL_FUZZ_INIT(); + +int main(int argc, char **argv) { + FILE *file = stdin; + if (argc > 1) { file = fopen(argv[1], "rb"); } + + // The following three lines are for normal fuzzing. + /* + char buf[16]; + char* p = fgets(buf, 16, file); + buf[15] = 0; + */ + + // The following line is also needed for shared memory testcase fuzzing + unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF; + int len = __AFL_FUZZ_TESTCASE_LEN; + + printf("{\"len\": %d}", len); + + return 0; +} \ No newline at end of file diff --git a/fuzzers/forkserver/forkserver_libafl_cc/src/main.rs b/fuzzers/forkserver/forkserver_libafl_cc/src/main.rs index c710563645..0df9721c2e 100644 --- a/fuzzers/forkserver/forkserver_libafl_cc/src/main.rs +++ b/fuzzers/forkserver/forkserver_libafl_cc/src/main.rs @@ -5,7 +5,7 @@ use clap::Parser; use libafl::{ corpus::{Corpus, InMemoryCorpus, OnDiskCorpus}, events::SimpleEventManager, - executors::{forkserver::ForkserverExecutor, HasObservers}, + executors::{forkserver::ForkserverExecutor, HasObservers, StdChildArgs}, feedback_and_fast, feedback_or, feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback}, fuzzer::{Fuzzer, StdFuzzer}, @@ -22,7 +22,7 @@ use libafl_bolts::{ rands::StdRand, shmem::{ShMem, ShMemProvider, UnixShMemProvider}, tuples::{tuple_list, Handled, Merge}, - AsSliceMut, TargetArgs, Truncate, + AsSliceMut, StdTargetArgs, Truncate, }; use libafl_targets::EDGES_MAP_DEFAULT_SIZE; use nix::sys::signal::Signal; diff --git a/fuzzers/forkserver/forkserver_simple/src/main.rs b/fuzzers/forkserver/forkserver_simple/src/main.rs index a5ae55a17c..ff60fc65f1 100644 --- a/fuzzers/forkserver/forkserver_simple/src/main.rs +++ b/fuzzers/forkserver/forkserver_simple/src/main.rs @@ -6,7 +6,7 @@ use libafl::{ HasMetadata, corpus::{Corpus, InMemoryCorpus, OnDiskCorpus}, events::SimpleEventManager, - executors::{HasObservers, forkserver::ForkserverExecutor}, + executors::{HasObservers, StdChildArgs, forkserver::ForkserverExecutor}, feedback_and_fast, feedback_or, feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback}, fuzzer::{Fuzzer, StdFuzzer}, @@ -19,7 +19,7 @@ use libafl::{ state::{HasCorpus, StdState}, }; use libafl_bolts::{ - AsSliceMut, TargetArgs, Truncate, current_nanos, + AsSliceMut, StdTargetArgs, Truncate, current_nanos, rands::StdRand, shmem::{ShMem, ShMemProvider, UnixShMemProvider}, tuples::{Handled, Merge, tuple_list}, diff --git a/fuzzers/forkserver/fuzzbench_forkserver/src/main.rs b/fuzzers/forkserver/fuzzbench_forkserver/src/main.rs index d458def788..0f0969d7d7 100644 --- a/fuzzers/forkserver/fuzzbench_forkserver/src/main.rs +++ b/fuzzers/forkserver/fuzzbench_forkserver/src/main.rs @@ -11,7 +11,10 @@ use clap::{Arg, ArgAction, Command}; use libafl::{ corpus::{Corpus, InMemoryOnDiskCorpus, OnDiskCorpus}, events::SimpleEventManager, - executors::forkserver::{ForkserverExecutor, SHM_CMPLOG_ENV_VAR}, + executors::{ + forkserver::{ForkserverExecutor, SHM_CMPLOG_ENV_VAR}, + StdChildArgs, + }, feedback_or, feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback}, fuzzer::{Fuzzer, StdFuzzer}, @@ -38,7 +41,7 @@ use libafl_bolts::{ rands::StdRand, shmem::{ShMem, ShMemProvider, UnixShMemProvider}, tuples::{tuple_list, Merge}, - AsSliceMut, TargetArgs, + AsSliceMut, StdTargetArgs, }; use libafl_targets::cmps::AFLppCmpLogMap; use nix::sys::signal::Signal; diff --git a/fuzzers/forkserver/fuzzbench_forkserver_cmplog/src/main.rs b/fuzzers/forkserver/fuzzbench_forkserver_cmplog/src/main.rs index 9570d5b522..a73f6c4101 100644 --- a/fuzzers/forkserver/fuzzbench_forkserver_cmplog/src/main.rs +++ b/fuzzers/forkserver/fuzzbench_forkserver_cmplog/src/main.rs @@ -11,7 +11,10 @@ use clap::{Arg, ArgAction, Command}; use libafl::{ corpus::{Corpus, InMemoryOnDiskCorpus, OnDiskCorpus}, events::SimpleEventManager, - executors::forkserver::{ForkserverExecutor, SHM_CMPLOG_ENV_VAR}, + executors::{ + forkserver::{ForkserverExecutor, SHM_CMPLOG_ENV_VAR}, + StdChildArgs, + }, feedback_or, feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback}, fuzzer::{Fuzzer, StdFuzzer}, @@ -37,7 +40,7 @@ use libafl_bolts::{ rands::StdRand, shmem::{ShMem, ShMemProvider, UnixShMemProvider}, tuples::{tuple_list, Handled, Merge}, - AsSliceMut, TargetArgs, + AsSliceMut, StdTargetArgs, }; use libafl_targets::{ cmps::{observers::AFLppCmpLogObserver, stages::AFLppCmplogTracingStage}, diff --git a/fuzzers/forkserver/fuzzbench_forkserver_sand/src/main.rs b/fuzzers/forkserver/fuzzbench_forkserver_sand/src/main.rs index 93393dfbf0..d57437edfe 100644 --- a/fuzzers/forkserver/fuzzbench_forkserver_sand/src/main.rs +++ b/fuzzers/forkserver/fuzzbench_forkserver_sand/src/main.rs @@ -14,6 +14,7 @@ use libafl::{ executors::{ forkserver::{ForkserverExecutor, SHM_CMPLOG_ENV_VAR}, sand::SANDExecutor, + StdChildArgs, }, feedback_or, feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback}, @@ -41,7 +42,7 @@ use libafl_bolts::{ rands::StdRand, shmem::{ShMem, ShMemProvider, UnixShMemProvider}, tuples::{tuple_list, Handled, Merge}, - AsSliceMut, TargetArgs, + AsSliceMut, StdTargetArgs, }; use libafl_targets::cmps::AFLppCmpLogMap; use nix::sys::signal::Signal; diff --git a/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs b/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs index 46931e3031..dd1e89ec56 100644 --- a/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs +++ b/fuzzers/forkserver/libafl-fuzz/src/fuzzer.rs @@ -16,7 +16,10 @@ use libafl::monitors::SimpleMonitor; use libafl::{ corpus::{CachedOnDiskCorpus, Corpus, OnDiskCorpus}, events::ProgressReporter, - executors::forkserver::{ForkserverExecutor, ForkserverExecutorBuilder, SHM_CMPLOG_ENV_VAR}, + executors::{ + forkserver::{ForkserverExecutor, ForkserverExecutorBuilder, SHM_CMPLOG_ENV_VAR}, + StdChildArgs, + }, feedback_and, feedback_or, feedback_or_fast, feedbacks::{ CaptureTimeoutFeedback, ConstFeedback, CrashFeedback, MaxMapFeedback, TimeFeedback, @@ -51,7 +54,7 @@ use libafl_bolts::{ rands::StdRand, shmem::{ShMem, ShMemProvider, UnixShMemProvider}, tuples::{tuple_list, Handled, Merge}, - AsSliceMut, TargetArgs, + AsSliceMut, StdTargetArgs, }; #[cfg(feature = "nyx")] use libafl_nyx::{executor::NyxExecutor, helper::NyxHelper, settings::NyxSettings}; diff --git a/fuzzers/structure_aware/forkserver_simple_nautilus/src/main.rs b/fuzzers/structure_aware/forkserver_simple_nautilus/src/main.rs index e8ac49e7ca..e56e5fc04e 100644 --- a/fuzzers/structure_aware/forkserver_simple_nautilus/src/main.rs +++ b/fuzzers/structure_aware/forkserver_simple_nautilus/src/main.rs @@ -5,7 +5,7 @@ use clap::Parser; use libafl::{ corpus::{InMemoryCorpus, OnDiskCorpus}, events::SimpleEventManager, - executors::{forkserver::ForkserverExecutor, HasObservers}, + executors::{forkserver::ForkserverExecutor, HasObservers, StdChildArgs}, feedback_and_fast, feedback_or, feedbacks::{ CrashFeedback, MaxMapFeedback, NautilusChunksMetadata, NautilusFeedback, TimeFeedback, @@ -29,7 +29,7 @@ use libafl_bolts::{ rands::StdRand, shmem::{ShMem, ShMemProvider, UnixShMemProvider}, tuples::{tuple_list, Handled}, - AsSliceMut, TargetArgs, Truncate, + AsSliceMut, StdTargetArgs, Truncate, }; use nix::sys::signal::Signal; diff --git a/fuzzers/structure_aware/libfuzzer_stb_image_concolic/fuzzer/src/main.rs b/fuzzers/structure_aware/libfuzzer_stb_image_concolic/fuzzer/src/main.rs index 9c2b6a8ee7..e9969e3b85 100644 --- a/fuzzers/structure_aware/libfuzzer_stb_image_concolic/fuzzer/src/main.rs +++ b/fuzzers/structure_aware/libfuzzer_stb_image_concolic/fuzzer/src/main.rs @@ -217,9 +217,11 @@ fn fuzz( let mut stages = tuple_list!( // Create a concolic trace ConcolicTracingStage::new( - TracingStage::new( - MyCommandConfigurator.into_executor(tuple_list!(concolic_observer)), - ), + TracingStage::new(MyCommandConfigurator.into_executor( + tuple_list!(concolic_observer), + None, + None + ),), concolic_ref, ), // Use the concolic trace for z3-based solving diff --git a/libafl/src/corpus/inmemory_ondisk.rs b/libafl/src/corpus/inmemory_ondisk.rs index f2327776ef..1dcbe494c8 100644 --- a/libafl/src/corpus/inmemory_ondisk.rs +++ b/libafl/src/corpus/inmemory_ondisk.rs @@ -533,7 +533,10 @@ mod tests { match try_create_new(&path) { Ok(None) => (), - Ok(_) => panic!("File {path:?} did not exist even though it should have?"), + Ok(_) => panic!( + "File {} did not exist even though it should have?", + &path.display() + ), Err(e) => panic!("An unexpected error occurred: {e}"), } drop(f); diff --git a/libafl/src/executors/command.rs b/libafl/src/executors/command.rs index 0930b79a9b..b74946a0ca 100644 --- a/libafl/src/executors/command.rs +++ b/libafl/src/executors/command.rs @@ -1,6 +1,7 @@ //! The command executor executes a sub program for each run #[cfg(all(feature = "intel_pt", target_os = "linux"))] use alloc::ffi::CString; +#[cfg(all(feature = "intel_pt", target_os = "linux"))] use alloc::vec::Vec; #[cfg(all(feature = "intel_pt", target_os = "linux"))] use core::ffi::CStr; @@ -13,18 +14,17 @@ use core::{ #[cfg(all(feature = "intel_pt", target_os = "linux"))] use std::os::fd::AsRawFd; use std::{ - ffi::{OsStr, OsString}, + ffi::OsStr, io::{Read, Write}, - os::unix::ffi::OsStrExt, - path::{Path, PathBuf}, + os::{fd::RawFd, unix::ffi::OsStrExt}, process::{Child, Command, Stdio}, }; #[cfg(all(feature = "intel_pt", target_os = "linux"))] use libafl_bolts::core_affinity::CoreId; use libafl_bolts::{ - AsSlice, InputLocation, TargetArgs, - tuples::{Handle, MatchName, RefIndexable}, + AsSlice, InputLocation, StdTargetArgs, StdTargetArgsInner, + tuples::{Handle, MatchName, MatchNameRef, RefIndexable}, }; #[cfg(all(feature = "intel_pt", target_os = "linux"))] use libc::STDIN_FILENO; @@ -46,7 +46,9 @@ use nix::{ #[cfg(all(feature = "intel_pt", target_os = "linux"))] use typed_builder::TypedBuilder; -use super::HasTimeout; +#[cfg(all(target_family = "unix", feature = "fork"))] +use super::forkserver::ConfigTarget; +use super::{HasTimeout, StdChildArgs, StdChildArgsInner}; #[cfg(target_os = "linux")] use crate::executors::hooks::ExecutorHooksTuple; use crate::{ @@ -55,9 +57,47 @@ use crate::{ inputs::HasTargetBytes, observers::{ObserversTuple, StdErrObserver, StdOutObserver}, state::HasExecutions, - std::borrow::ToOwned, }; +/// How do we capture stdout/stderr. Not intended for public use. +#[derive(Debug, Default)] +#[allow(dead_code)] +enum StdCommandCaptureMethod { + Fd(RawFd), + #[default] + Pipe, +} + +impl StdCommandCaptureMethod { + fn pipe_capture(cmd: &mut Command, stdout: bool) { + if stdout { + cmd.stdout(Stdio::piped()); + } else { + cmd.stderr(Stdio::piped()); + } + } + + fn pre_capture(&self, cmd: &mut Command, stdout: bool) { + #[cfg(feature = "fork")] + { + if let Self::Fd(old) = self { + if stdout { + cmd.setdup2(*old, libc::STDOUT_FILENO); + cmd.stdout(Stdio::null()); + } else { + cmd.setdup2(*old, libc::STDERR_FILENO); + cmd.stderr(Stdio::null()); + } + } else { + Self::pipe_capture(cmd, stdout); + } + } + + #[cfg(not(feature = "fork"))] + Self::pipe_capture(cmd, stdout); + } +} + /// A simple Configurator that takes the most common parameters /// Writes the input either to stdio or to a file /// Use [`CommandExecutor::builder()`] to use this configurator. @@ -66,8 +106,8 @@ pub struct StdCommandConfigurator { /// If set to true, the child output will remain visible /// By default, the child output is hidden to increase execution speed debug_child: bool, - stdout_observer: Option>, - stderr_observer: Option>, + stdout_cap: Option, + stderr_cap: Option, timeout: Duration, /// true: input gets delivered via stdink input_location: InputLocation, @@ -79,30 +119,26 @@ impl CommandConfigurator for StdCommandConfigurator where I: HasTargetBytes, { - fn stdout_observer(&self) -> Option> { - self.stdout_observer.clone() - } - - fn stderr_observer(&self) -> Option> { - self.stderr_observer.clone() - } - fn spawn_child(&mut self, input: &I) -> Result { + let mut cmd = Command::new(self.command.get_program()); match &mut self.input_location { InputLocation::Arg { argnum } => { let args = self.command.get_args(); - let mut cmd = Command::new(self.command.get_program()); - if !self.debug_child { + if self.debug_child { + cmd.stdout(Stdio::inherit()); + } else if let Some(cap) = &self.stdout_cap { + cap.pre_capture(&mut cmd, true); + } else { cmd.stdout(Stdio::null()); - cmd.stderr(Stdio::null()); } - if self.stdout_observer.is_some() { - cmd.stdout(Stdio::piped()); - } - if self.stderr_observer.is_some() { - cmd.stderr(Stdio::piped()); + if self.debug_child { + cmd.stderr(Stdio::inherit()); + } else if let Some(cap) = &self.stderr_cap { + cap.pre_capture(&mut cmd, false); + } else { + cmd.stderr(Stdio::null()); } for (i, arg) in args.enumerate() { @@ -269,6 +305,8 @@ pub struct CommandExecutor { configurer: T, /// The observers used by this executor observers: OT, + stdout_observer: Option>, + stderr_observer: Option>, hooks: HT, phantom: PhantomData<(C, I, S)>, } @@ -302,6 +340,8 @@ where .field("inner", &self.configurer) .field("observers", &self.observers) .field("hooks", &self.hooks) + .field("stdout_observer", &self.stdout_observer) + .field("stderr_observer", &self.stderr_observer) .finish() } } @@ -323,9 +363,8 @@ where fn execute_input_with_command(&mut self, state: &mut S, input: &I) -> Result { use wait_timeout::ChildExt; + self.observers_mut().pre_exec_all(state, input)?; *state.executions_mut() += 1; - self.observers.pre_exec_child_all(state, input)?; - let mut child = self.configurer.spawn_child(input)?; let exit_kind = child @@ -341,31 +380,28 @@ where ExitKind::Timeout }); - self.observers - .post_exec_child_all(state, input, &exit_kind)?; + // Manualy update stdout/stderr here if we use piped implementation. + // Reason of not putting into state and pass by post_exec_all is that + // - Save extra at least two hashmap lookups since we already know the handle + // - Doesn't pose HasNamedMetadata bound on S (note we might have many stdout/stderr observers) + if let Some(mut stderr) = child.stderr { + if let Some(stderr_handle) = self.stderr_observer.clone() { + let mut buf = vec![]; + stderr.read_to_end(&mut buf)?; + self.observers_mut().index_mut(&stderr_handle).observe(buf); + } + } - if let Some(h) = &mut self.configurer.stdout_observer() { - let mut stdout = Vec::new(); - child.stdout.as_mut().ok_or_else(|| { - Error::illegal_state( - "Observer tries to read stderr, but stderr was not `Stdio::pipe` in CommandExecutor", - ) - })?.read_to_end(&mut stdout)?; - let mut observers = self.observers_mut(); - let obs = observers.index_mut(h); - obs.observe(&stdout); - } - if let Some(h) = &mut self.configurer.stderr_observer() { - let mut stderr = Vec::new(); - child.stderr.as_mut().ok_or_else(|| { - Error::illegal_state( - "Observer tries to read stderr, but stderr was not `Stdio::pipe` in CommandExecutor", - ) - })?.read_to_end(&mut stderr)?; - let mut observers = self.observers_mut(); - let obs = observers.index_mut(h); - obs.observe(&stderr); + if let Some(mut stdout) = child.stdout { + if let Some(stdout_handle) = self.stdout_observer.clone() { + let mut buf = vec![]; + stdout.read_to_end(&mut buf)?; + self.observers_mut().index_mut(&stdout_handle).observe(buf); + } } + + self.observers_mut() + .post_exec_child_all(state, input, &exit_kind)?; Ok(exit_kind) } } @@ -497,48 +533,27 @@ where /// The builder for a default [`CommandExecutor`] that should fit most use-cases. #[derive(Debug, Clone)] pub struct CommandExecutorBuilder { - stdout: Option>, - stderr: Option>, - debug_child: bool, - program: Option, - args: Vec, - input_location: InputLocation, - cwd: Option, - envs: Vec<(OsString, OsString)>, - timeout: Duration, + target_inner: StdTargetArgsInner, + child_env_inner: StdChildArgsInner, } -impl TargetArgs for CommandExecutorBuilder { - fn arguments_ref(&self) -> &Vec { - &self.args +impl StdTargetArgs for CommandExecutorBuilder { + fn inner(&self) -> &StdTargetArgsInner { + &self.target_inner } - fn arguments_mut(&mut self) -> &mut Vec { - &mut self.args + fn inner_mut(&mut self) -> &mut StdTargetArgsInner { + &mut self.target_inner + } +} + +impl StdChildArgs for CommandExecutorBuilder { + fn inner(&self) -> &StdChildArgsInner { + &self.child_env_inner } - fn envs_ref(&self) -> &Vec<(OsString, OsString)> { - &self.envs - } - - fn envs_mut(&mut self) -> &mut Vec<(OsString, OsString)> { - &mut self.envs - } - - fn program_ref(&self) -> &Option { - &self.program - } - - fn program_mut(&mut self) -> &mut Option { - &mut self.program - } - - fn input_location_ref(&self) -> &InputLocation { - &self.input_location - } - - fn input_location_mut(&mut self) -> &mut InputLocation { - &mut self.input_location + fn inner_mut(&mut self) -> &mut StdChildArgsInner { + &mut self.child_env_inner } } @@ -553,49 +568,11 @@ impl CommandExecutorBuilder { #[must_use] fn new() -> CommandExecutorBuilder { CommandExecutorBuilder { - stdout: None, - stderr: None, - program: None, - args: vec![], - input_location: InputLocation::StdIn, - cwd: None, - envs: vec![], - timeout: Duration::from_secs(5), - debug_child: false, + target_inner: StdTargetArgsInner::default(), + child_env_inner: StdChildArgsInner::default(), } } - /// Sets the stdout observer - pub fn stdout_observer(&mut self, stdout: Handle) -> &mut Self { - self.stdout = Some(stdout); - self - } - - /// Sets the stderr observer - pub fn stderr_observer(&mut self, stderr: Handle) -> &mut Self { - self.stderr = Some(stderr); - self - } - - /// Sets the working directory for the child process. - pub fn current_dir>(&mut self, dir: P) -> &mut CommandExecutorBuilder { - self.cwd = Some(dir.as_ref().to_owned()); - self - } - - /// If set to true, the child's output won't be redirecited to `/dev/null`. - /// Defaults to `false`. - pub fn debug_child(&mut self, debug_child: bool) -> &mut CommandExecutorBuilder { - self.debug_child = debug_child; - self - } - - /// Sets the execution timeout duration. - pub fn timeout(&mut self, timeout: Duration) -> &mut CommandExecutorBuilder { - self.timeout = timeout; - self - } - /// Builds the `CommandExecutor` pub fn build( &self, @@ -605,14 +582,14 @@ impl CommandExecutorBuilder { I: HasTargetBytes, OT: MatchName + ObserversTuple, { - let Some(program) = &self.program else { + let Some(program) = &self.target_inner.program else { return Err(Error::illegal_argument( "CommandExecutor::builder: no program set!", )); }; let mut command = Command::new(program); - match &self.input_location { + match &self.target_inner.input_location { InputLocation::StdIn => { command.stdin(Stdio::piped()); } @@ -620,40 +597,77 @@ impl CommandExecutorBuilder { command.stdin(Stdio::null()); } } - command.args(&self.args); + command.args(&self.target_inner.arguments); command.envs( - self.envs + self.target_inner + .envs .iter() .map(|(k, v)| (k.as_os_str(), v.as_os_str())), ); - if let Some(cwd) = &self.cwd { + if let Some(cwd) = &self.child_env_inner.current_directory { command.current_dir(cwd); } - if !self.debug_child { - command.stdout(Stdio::null()); - command.stderr(Stdio::null()); + + let stdout_cap = self.child_env_inner.stdout_observer.as_ref().map(|hdl| { + observers + .get(hdl) + .as_ref() + .expect("stdout observer not in observers tuple") + .as_raw_fd() + .map(StdCommandCaptureMethod::Fd) + .unwrap_or_default() + }); + + let stderr_cap = self.child_env_inner.stderr_observer.as_ref().map(|hdl| { + observers + .get(hdl) + .as_ref() + .expect("stderr observer not in observers tuple") + .as_raw_fd() + .map(StdCommandCaptureMethod::Fd) + .unwrap_or_default() + }); + + if self.child_env_inner.debug_child { + command.stdout(Stdio::piped()); + command.stderr(Stdio::piped()); + } else { + if let Some(cap) = &stdout_cap { + cap.pre_capture(&mut command, true); + } else { + command.stdout(Stdio::null()); + } + + if let Some(cap) = &stderr_cap { + cap.pre_capture(&mut command, false); + } else { + command.stderr(Stdio::null()); + } } - if self.stdout.is_some() { + if self.child_env_inner.stdout_observer.is_some() { command.stdout(Stdio::piped()); } - if self.stderr.is_some() { + if self.child_env_inner.stderr_observer.is_some() { command.stderr(Stdio::piped()); } let configurator = StdCommandConfigurator { - debug_child: self.debug_child, - stdout_observer: self.stdout.clone(), - stderr_observer: self.stderr.clone(), - input_location: self.input_location.clone(), - timeout: self.timeout, + debug_child: self.child_env_inner.debug_child, + stdout_cap, + stderr_cap, + input_location: self.target_inner.input_location.clone(), + timeout: self.child_env_inner.timeout, command, }; + Ok( >::into_executor::( configurator, observers, + self.child_env_inner.stdout_observer.clone(), + self.child_env_inner.stderr_observer.clone(), ), ) } @@ -705,7 +719,7 @@ impl CommandExecutorBuilder { /// where /// S: HasExecutions, /// { -/// MyExecutor.into_executor(()) +/// MyExecutor.into_executor((), None, None) /// } /// ``` pub trait CommandConfigurator: Sized { @@ -739,11 +753,18 @@ pub trait CommandConfigurator: Sized { } /// Create an `Executor` from this `CommandConfigurator`. - fn into_executor(self, observers: OT) -> CommandExecutor { + fn into_executor( + self, + observers: OT, + stdout_observer: Option>, + stderr_observer: Option>, + ) -> CommandExecutor { CommandExecutor { configurer: self, observers, hooks: (), + stderr_observer, + stdout_observer, phantom: PhantomData, } } @@ -753,11 +774,15 @@ pub trait CommandConfigurator: Sized { self, observers: OT, hooks: HT, + stdout_observer: Option>, + stderr_observer: Option>, ) -> CommandExecutor { CommandExecutor { configurer: self, observers, hooks, + stderr_observer, + stdout_observer, phantom: PhantomData, } } @@ -781,17 +806,19 @@ fn waitpid_filtered(pid: Pid, options: Option) -> Result> = + SimpleEventManager::new(SimpleMonitor::new(|status| { + log::info!("{status}"); + })); + + let stdout = StdOutObserver::new("stdout".into()).unwrap(); + let handle = stdout.handle(); + let executor = CommandExecutor::builder() + .program("ls") + .stdout_observer(handle.clone()) + .input(InputLocation::Arg { argnum: 0 }); + let executor = executor.build(tuple_list!(stdout)); + let mut executor = executor.unwrap(); + + executor + .run_target( + &mut NopFuzzer::new(), + &mut NopState::::new(), + &mut mgr, + &BytesInput::new(b".".to_vec()), + ) + .unwrap(); + + assert!(executor.observers.0.output.is_some()); + } } diff --git a/libafl/src/executors/forkserver.rs b/libafl/src/executors/forkserver.rs index d4d1bc7d63..45a9c5f57a 100644 --- a/libafl/src/executors/forkserver.rs +++ b/libafl/src/executors/forkserver.rs @@ -14,17 +14,18 @@ use std::{ fd::{AsRawFd, BorrowedFd}, unix::{io::RawFd, process::CommandExt}, }, + path::PathBuf, process::{Child, Command, Stdio}, }; #[cfg(feature = "regex")] -use libafl_bolts::tuples::{Handle, Handled, MatchNameRef}; +use libafl_bolts::tuples::{Handle, Handled}; use libafl_bolts::{ - AsSlice, AsSliceMut, InputLocation, TargetArgs, Truncate, + AsSlice, AsSliceMut, InputLocation, StdTargetArgs, StdTargetArgsInner, Truncate, fs::{InputFile, get_unique_std_input_file}, os::{dup2, pipes::Pipe}, shmem::{ShMem, ShMemProvider, UnixShMem, UnixShMemProvider}, - tuples::{Prepend, RefIndexable}, + tuples::{MatchNameRef, Prepend, RefIndexable}, }; use libc::RLIM_INFINITY; use nix::{ @@ -37,7 +38,7 @@ use nix::{ unistd::Pid, }; -use super::HasTimeout; +use super::{HasTimeout, StdChildArgs, StdChildArgsInner}; #[cfg(feature = "regex")] use crate::observers::{ AsanBacktraceObserver, get_asan_runtime_flags, get_asan_runtime_flags_with_log_path, @@ -161,8 +162,6 @@ pub trait ConfigTarget { fn setlimit(&mut self, memlimit: u64) -> &mut Self; /// enables core dumps (rlimit = infinity) fn set_coredump(&mut self, enable: bool) -> &mut Self; - /// Sets the stdin - fn setstdin(&mut self, fd: RawFd, use_stdin: bool) -> &mut Self; /// Sets the AFL forkserver pipes fn setpipe( &mut self, @@ -171,6 +170,8 @@ pub trait ConfigTarget { ctl_read: RawFd, ctl_write: RawFd, ) -> &mut Self; + /// dup2 the specific fd, used for stdio + fn setdup2(&mut self, old_fd: RawFd, new_fd: RawFd) -> &mut Self; } impl ConfigTarget for Command { @@ -216,23 +217,6 @@ impl ConfigTarget for Command { unsafe { self.pre_exec(func) } } - fn setstdin(&mut self, fd: RawFd, use_stdin: bool) -> &mut Self { - if use_stdin { - let func = move || { - match dup2(fd, libc::STDIN_FILENO) { - Ok(()) => (), - Err(_) => { - return Err(io::Error::last_os_error()); - } - } - Ok(()) - }; - unsafe { self.pre_exec(func) } - } else { - self - } - } - #[expect(trivial_numeric_casts)] fn setlimit(&mut self, memlimit: u64) -> &mut Self { if memlimit == 0 { @@ -277,6 +261,19 @@ impl ConfigTarget for Command { // This calls our non-shady function from above. unsafe { self.pre_exec(func) } } + + fn setdup2(&mut self, old_fd: RawFd, new_fd: RawFd) -> &mut Self { + let func = move || { + let ret = unsafe { libc::dup2(old_fd, new_fd) }; + if ret < 0 { + return Err(io::Error::last_os_error()); + } + Ok(()) + }; + // # Safety + // This calls our non-shady function from above. + unsafe { self.pre_exec(func) } + } } /// The [`Forkserver`] is communication channel with a child process that forks on request of the fuzzer. @@ -358,6 +355,9 @@ impl Forkserver { coverage_map_size: Option, debug_output: bool, kill_signal: Signal, + stdout_memfd: Option, + stderr_memfd: Option, + cwd: Option, ) -> Result { let Some(coverage_map_size) = coverage_map_size else { return Err(Error::unknown( @@ -385,19 +385,32 @@ impl Forkserver { let mut st_pipe = Pipe::new().unwrap(); let mut ctl_pipe = Pipe::new().unwrap(); - let (stdout, stderr) = if debug_output { - (Stdio::inherit(), Stdio::inherit()) - } else { - (Stdio::null(), Stdio::null()) - }; - let mut command = Command::new(target); // Setup args, stdio - command - .args(args) - .stdin(Stdio::null()) - .stdout(stdout) - .stderr(stderr); + command.args(args); + if use_stdin { + command.setdup2(input_filefd, libc::STDIN_FILENO); + } else { + command.stdin(Stdio::null()); + } + + if debug_output { + command.stdout(Stdio::inherit()); + } else if let Some(fd) = &stdout_memfd { + command.setdup2(*fd, libc::STDOUT_FILENO); + command.stdout(Stdio::null()); + } else { + command.stdout(Stdio::null()); + } + + if debug_output { + command.stderr(Stdio::inherit()); + } else if let Some(fd) = &stderr_memfd { + command.setdup2(*fd, libc::STDERR_FILENO); + command.stderr(Stdio::null()); + } else { + command.stderr(Stdio::null()); + } command.env("AFL_MAP_SIZE", format!("{coverage_map_size}")); @@ -422,13 +435,16 @@ impl Forkserver { #[cfg(not(feature = "regex"))] let _ = dump_asan_logs; + if let Some(cwd) = cwd { + command.current_dir(cwd); + } + let fsrv_handle = match command .env("LD_BIND_NOW", "1") .envs(envs) .setlimit(memlimit) .set_coredump(afl_debug) .setsid() - .setstdin(input_filefd, use_stdin) .setpipe( st_pipe.read_end().unwrap(), st_pipe.write_end().unwrap(), @@ -798,58 +814,40 @@ where /// The builder for `ForkserverExecutor` #[derive(Debug)] -#[expect(clippy::struct_excessive_bools)] pub struct ForkserverExecutorBuilder<'a, SP> { - program: Option, - arguments: Vec, - envs: Vec<(OsString, OsString)>, - debug_child: bool, + target_inner: StdTargetArgsInner, + child_env_inner: StdChildArgsInner, uses_shmem_testcase: bool, is_persistent: bool, is_deferred_frksrv: bool, autotokens: Option<&'a mut Tokens>, - input_location: InputLocation, shmem_provider: Option<&'a mut SP>, max_input_size: usize, min_input_size: usize, map_size: Option, kill_signal: Option, - timeout: Option, #[cfg(feature = "regex")] asan_obs: Option>, crash_exitcode: Option, } -impl TargetArgs for ForkserverExecutorBuilder<'_, SP> { - fn arguments_ref(&self) -> &Vec { - &self.arguments - } - fn arguments_mut(&mut self) -> &mut Vec { - &mut self.arguments +impl StdChildArgs for ForkserverExecutorBuilder<'_, SP> { + fn inner(&self) -> &StdChildArgsInner { + &self.child_env_inner } - fn envs_ref(&self) -> &Vec<(OsString, OsString)> { - &self.envs + fn inner_mut(&mut self) -> &mut StdChildArgsInner { + &mut self.child_env_inner + } +} + +impl StdTargetArgs for ForkserverExecutorBuilder<'_, SP> { + fn inner(&self) -> &StdTargetArgsInner { + &self.target_inner } - fn envs_mut(&mut self) -> &mut Vec<(OsString, OsString)> { - &mut self.envs - } - - fn program_ref(&self) -> &Option { - &self.program - } - - fn program_mut(&mut self) -> &mut Option { - &mut self.program - } - - fn input_location_ref(&self) -> &InputLocation { - &self.input_location - } - - fn input_location_mut(&mut self) -> &mut InputLocation { - &mut self.input_location + fn inner_mut(&mut self) -> &mut StdTargetArgsInner { + &mut self.target_inner } fn arg_input_arg(self) -> Self { @@ -875,13 +873,13 @@ where where OT: ObserversTuple, { - let (forkserver, input_file, map) = self.build_helper()?; + let (forkserver, input_file, map) = self.build_helper(&observers)?; - let target = self.program.take().unwrap(); + let target = self.target_inner.program.take().unwrap(); log::info!( "ForkserverExecutor: program: {:?}, arguments: {:?}, use_stdin: {:?}", target, - self.arguments.clone(), + self.target_inner.arguments.clone(), self.use_stdin() ); @@ -891,10 +889,7 @@ where )); } - let timeout: TimeSpec = match self.timeout { - Some(t) => t.into(), - None => Duration::from_millis(5000).into(), - }; + let timeout: TimeSpec = self.child_env_inner.timeout.into(); if self.min_input_size > self.max_input_size { return Err(Error::illegal_argument( format!( @@ -907,7 +902,7 @@ where Ok(ForkserverExecutor { target, - args: self.arguments.clone(), + args: self.target_inner.arguments.clone(), input_file, uses_shmem_testcase: self.uses_shmem_testcase, forkserver, @@ -940,13 +935,13 @@ where MO: MapObserver + Truncate, // TODO maybe enforce Entry = u8 for the cov map OT: ObserversTuple + Prepend, { - let (forkserver, input_file, map) = self.build_helper()?; + let (forkserver, input_file, map) = self.build_helper(&other_observers)?; - let target = self.program.take().unwrap(); + let target = self.target_inner.program.take().unwrap(); log::info!( "ForkserverExecutor: program: {:?}, arguments: {:?}, use_stdin: {:?}, map_size: {:?}", target, - self.arguments.clone(), + self.target_inner.arguments.clone(), self.use_stdin(), self.map_size ); @@ -963,14 +958,11 @@ where )); } - let timeout: TimeSpec = match self.timeout { - Some(t) => t.into(), - None => Duration::from_millis(5000).into(), - }; + let timeout: TimeSpec = self.child_env_inner.timeout.into(); Ok(ForkserverExecutor { target, - args: self.arguments.clone(), + args: self.target_inner.arguments.clone(), input_file, uses_shmem_testcase: self.uses_shmem_testcase, forkserver, @@ -991,8 +983,14 @@ where } #[expect(clippy::pedantic)] - fn build_helper(&mut self) -> Result<(Forkserver, InputFile, Option), Error> { - let input_file = match &self.input_location { + fn build_helper( + &mut self, + obs: &OT, + ) -> Result<(Forkserver, InputFile, Option), Error> + where + OT: ObserversTuple, + { + let input_file = match &self.target_inner.input_location { InputLocation::StdIn => InputFile::create(OsString::from(get_unique_std_input_file()))?, InputLocation::Arg { argnum: _ } => { return Err(Error::illegal_argument( @@ -1019,11 +1017,11 @@ where } }; - let mut forkserver = match &self.program { + let mut forkserver = match &self.target_inner.program { Some(t) => Forkserver::new( t.clone(), - self.arguments.clone(), - self.envs.clone(), + self.target_inner.arguments.clone(), + self.target_inner.envs.clone(), input_file.as_raw_fd(), self.use_stdin(), 0, @@ -1031,8 +1029,23 @@ where self.is_deferred_frksrv, self.has_asan_obs(), self.map_size, - self.debug_child, + self.child_env_inner.debug_child, self.kill_signal.unwrap_or(KILL_SIGNAL_DEFAULT), + self.child_env_inner.stdout_observer.as_ref().map(|t| { + obs.get(t) + .as_ref() + .expect("stdout observer not passed in the builder") + .as_raw_fd() + .expect("only memory fd backend is allowed for forkserver executor") + }), + self.child_env_inner.stderr_observer.as_ref().map(|t| { + obs.get(t) + .as_ref() + .expect("stderr observer not passed in the builder") + .as_raw_fd() + .expect("only memory fd backend is allowed for forkserver executor") + }), + self.child_env_inner.current_directory.clone(), )?, None => { return Err(Error::illegal_argument( @@ -1278,13 +1291,6 @@ where self } - #[must_use] - /// set the timeout for the executor - pub fn timeout(mut self, timeout: Duration) -> Self { - self.timeout = Some(timeout); - self - } - /// Set the max input size #[must_use] pub fn max_input_size(mut self, size: usize) -> Self { @@ -1299,13 +1305,6 @@ where self } - /// If `debug_child` is set, the child will print to `stdout`/`stderr`. - #[must_use] - pub fn debug_child(mut self, debug_child: bool) -> Self { - self.debug_child = debug_child; - self - } - /// Call this if you want to run it under persistent mode; default is false #[must_use] pub fn is_persistent(mut self, is_persistent: bool) -> Self { @@ -1366,21 +1365,17 @@ impl<'a> ForkserverExecutorBuilder<'a, UnixShMemProvider> { #[must_use] pub fn new() -> ForkserverExecutorBuilder<'a, UnixShMemProvider> { ForkserverExecutorBuilder { - program: None, - arguments: vec![], - envs: vec![], - debug_child: false, + target_inner: StdTargetArgsInner::default(), + child_env_inner: StdChildArgsInner::default(), uses_shmem_testcase: false, is_persistent: false, is_deferred_frksrv: false, autotokens: None, - input_location: InputLocation::StdIn, shmem_provider: None, map_size: None, max_input_size: MAX_INPUT_SIZE_DEFAULT, min_input_size: MIN_INPUT_SIZE_DEFAULT, kill_signal: None, - timeout: None, #[cfg(feature = "regex")] asan_obs: None, crash_exitcode: None, @@ -1398,20 +1393,16 @@ impl<'a> ForkserverExecutorBuilder<'a, UnixShMemProvider> { // Set the new provider shmem_provider: Some(shmem_provider), // Copy all other values from the old Builder - program: self.program, - arguments: self.arguments, - envs: self.envs, - debug_child: self.debug_child, + target_inner: self.target_inner, + child_env_inner: self.child_env_inner, uses_shmem_testcase: self.uses_shmem_testcase, is_persistent: self.is_persistent, is_deferred_frksrv: self.is_deferred_frksrv, autotokens: self.autotokens, - input_location: InputLocation::StdIn, map_size: self.map_size, max_input_size: self.max_input_size, min_input_size: self.min_input_size, kill_signal: self.kill_signal, - timeout: self.timeout, #[cfg(feature = "regex")] asan_obs: self.asan_obs, crash_exitcode: self.crash_exitcode, @@ -1443,7 +1434,11 @@ where ) -> Result { let converter = fuzzer.converter_mut(); let bytes = converter.to_bytes(input); - self.execute_input(state, bytes.as_slice()) + self.observers_mut().pre_exec_child_all(state, input)?; + let exit = self.execute_input(state, bytes.as_slice())?; + self.observers_mut() + .post_exec_child_all(state, input, &exit)?; + Ok(exit) } } @@ -1481,7 +1476,7 @@ mod tests { use std::ffi::OsString; use libafl_bolts::{ - AsSliceMut, TargetArgs, + AsSliceMut, StdTargetArgs, shmem::{ShMem, ShMemProvider, UnixShMemProvider}, tuples::tuple_list, }; @@ -1490,7 +1485,10 @@ mod tests { use crate::{ Error, corpus::NopCorpus, - executors::forkserver::{FAILED_TO_START_FORKSERVER_MSG, ForkserverExecutor}, + executors::{ + StdChildArgs, + forkserver::{FAILED_TO_START_FORKSERVER_MSG, ForkserverExecutor}, + }, inputs::BytesInput, observers::{ConstMapObserver, HitcountsMapObserver}, }; diff --git a/libafl/src/executors/mod.rs b/libafl/src/executors/mod.rs index 184e1079a7..eb076881cc 100644 --- a/libafl/src/executors/mod.rs +++ b/libafl/src/executors/mod.rs @@ -2,6 +2,8 @@ use alloc::vec::Vec; use core::{fmt::Debug, time::Duration}; +#[cfg(feature = "std")] +use std::path::PathBuf; pub use combined::CombinedExecutor; #[cfg(all(feature = "std", unix))] @@ -14,12 +16,16 @@ pub use inprocess::InProcessExecutor; pub use inprocess_fork::InProcessForkExecutor; #[cfg(unix)] use libafl_bolts::os::unix_signals::Signal; +#[cfg(feature = "std")] +use libafl_bolts::tuples::Handle; use libafl_bolts::tuples::RefIndexable; use serde::{Deserialize, Serialize}; pub use shadow::ShadowExecutor; pub use with_observers::WithObservers; use crate::Error; +#[cfg(feature = "std")] +use crate::observers::{StdErrObserver, StdOutObserver}; pub mod combined; #[cfg(all(feature = "std", unix))] @@ -227,6 +233,87 @@ pub fn common_signals() -> Vec { ] } +#[cfg(feature = "std")] +/// The inner shared members of [`StdChildArgs`] +#[derive(Debug, Clone)] +pub struct StdChildArgsInner { + /// The timeout of the children + pub timeout: Duration, + /// The stderr handle of the children + pub stderr_observer: Option>, + /// The stdout handle of the children + pub stdout_observer: Option>, + /// The current directory of the spawned children + pub current_directory: Option, + /// Whether debug child by inheriting stdout/stderr + pub debug_child: bool, +} + +#[cfg(feature = "std")] +impl Default for StdChildArgsInner { + fn default() -> Self { + Self { + timeout: Duration::from_millis(5000), + stderr_observer: None, + stdout_observer: None, + current_directory: None, + debug_child: false, + } + } +} + +#[cfg(feature = "std")] +/// The shared implementation for children with stdout/stderr/timeouts. +pub trait StdChildArgs: Sized { + /// The inner struct of child environment. + fn inner(&self) -> &StdChildArgsInner; + + /// The mutable inner struct of child environment. + fn inner_mut(&mut self) -> &mut StdChildArgsInner; + + #[must_use] + /// Sets the execution timeout duration. + fn timeout(mut self, timeout: Duration) -> Self { + self.inner_mut().timeout = timeout; + self + } + + #[must_use] + /// Sets the stdout observer + fn stdout_observer(mut self, stdout: Handle) -> Self { + self.inner_mut().stdout_observer = Some(stdout); + self + } + + #[must_use] + /// Sets the stderr observer + fn stderr_observer(mut self, stderr: Handle) -> Self { + self.inner_mut().stderr_observer = Some(stderr); + self + } + + #[must_use] + /// Sets the working directory for the child process. + fn current_dir(mut self, current_dir: PathBuf) -> Self { + self.inner_mut().current_directory = Some(current_dir); + self + } + + #[must_use] + /// If set to true, the child's output won't be redirecited to `/dev/null` and will go to parent's stdout/stderr + /// Defaults to `false`. + fn debug_child(mut self, debug_child: bool) -> Self { + if debug_child { + assert!( + self.inner().stderr_observer.is_none() && self.inner().stdout_observer.is_none(), + "you can not set debug_child when you have stderr_observer or stdout_observer" + ); + } + self.inner_mut().debug_child = debug_child; + self + } +} + #[cfg(test)] /// Tester for executor pub mod test { diff --git a/libafl/src/observers/stdio.rs b/libafl/src/observers/stdio.rs index 4ccc1dc501..44e877b8c9 100644 --- a/libafl/src/observers/stdio.rs +++ b/libafl/src/observers/stdio.rs @@ -4,14 +4,18 @@ //! The executor must explicitly support these observers. #![cfg_attr( unix, - doc = r"For example, they are supported on the [`crate::executors::CommandExecutor`]." + doc = r"For example, they are supported on the [`crate::executors::CommandExecutor`] and [`crate::executors::ForkserverExecutor`]." )] -use alloc::{borrow::Cow, vec::Vec}; +use alloc::{borrow::Cow, string::ToString, vec::Vec}; use core::marker::PhantomData; +use std::{ + fs::File, + io::{Read, Seek, SeekFrom}, +}; use libafl_bolts::Named; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize}; use crate::{Error, observers::Observer}; @@ -26,7 +30,7 @@ use crate::{Error, observers::Observer}; /// Error, Fuzzer, StdFuzzer, /// corpus::{Corpus, InMemoryCorpus, Testcase}, /// events::{EventFirer, NopEventManager}, -/// executors::{CommandExecutor, ExitKind}, +/// executors::{StdChildArgs, CommandExecutor, ExitKind}, /// feedbacks::{Feedback, StateInitializer}, /// inputs::BytesInput, /// mutators::{MutationResult, NopMutator}, @@ -36,7 +40,7 @@ use crate::{Error, observers::Observer}; /// }; /// use libafl_bolts::{ /// Named, current_nanos, -/// TargetArgs, +/// StdTargetArgs, /// rands::StdRand, /// tuples::{Handle, Handled, MatchNameRef, tuple_list}, /// }; @@ -87,8 +91,8 @@ use crate::{Error, observers::Observer}; /// let input_text = "Hello, World!"; /// let encoded_input_text = "SGVsbG8sIFdvcmxkIQo="; /// -/// let stdout_observer = StdOutObserver::new("stdout-observer"); -/// let stderr_observer = StdErrObserver::new("stderr-observer"); +/// let stdout_observer = StdOutObserver::new("stdout-observer".into()).unwrap(); +/// let stderr_observer = StdErrObserver::new("stderr-observer".into()).unwrap(); /// /// let mut feedback = ExportStdXObserver { /// stdout_observer: stdout_observer.handle(), @@ -170,16 +174,30 @@ use crate::{Error, observers::Observer}; /// } /// ``` /// -#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize)] pub struct OutputObserver { /// The name of the observer. pub name: Cow<'static, str>, /// The captured stdout/stderr data during last execution. pub output: Option>, + #[serde(skip_serializing, deserialize_with = "new_file::<_, T>")] + /// File backend of the memory to capture output, if [`None`] we use portable piped output + pub file: Option, + #[serde(skip)] /// Phantom data to hold the stream type phantom: PhantomData, } +/// Blanket implementation for a [`std::fs::File`]. Fortunately the contents of the file +/// is transient and thus we can safely create a new one on deserialization (and skip it) +/// when doing serialization +fn new_file<'de, D, T>(_d: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + OutputObserver::::file().map_err(|e| serde::de::Error::custom(e.to_string())) +} + /// Marker traits to mark stdout for the `OutputObserver` #[derive(Debug, Clone)] pub struct StdOutMarker; @@ -189,19 +207,80 @@ pub struct StdOutMarker; pub struct StdErrMarker; impl OutputObserver { - /// Create a new `OutputObserver` with the given name. - #[must_use] - pub fn new(name: &'static str) -> Self { - Self { - name: Cow::from(name), + // This is the best we can do on macOS because + // - macos doesn't have memfd_create + // - fd returned from shm_open can't be written (https://stackoverflow.com/questions/73752631/cant-write-to-fd-from-shm-open-on-macos) + // - there is even no native tmpfs implementation! + // therefore we create a file and immediately remove it to get a writtable fd. + // + // In most cases, capturing stdout/stderr every loop is very slow and mostly for debugging purpose and thus this should be acceptable. + #[cfg(target_os = "macos")] + fn file() -> Result, Error> { + let fp = File::create_new("fsrvmemfd")?; + nix::unistd::unlink("fsrvmemfd")?; + Ok(Some(fp)) + } + + /// Cool, we can have [`MemfdShMemProvider`] to create a memfd. + #[cfg(target_os = "linux")] + fn file() -> Result, Error> { + Ok(Some( + libafl_bolts::shmem::unix_shmem::memfd::MemfdShMemProvider::new_file()?, + )) + } + + /// This will use standard but portable pipe mechanism to capture outputs + #[cfg(not(any(target_os = "linux", target_os = "macos")))] + pub fn file() -> Result, Error> { + Ok(None) + } + + /// Create a new [`OutputObserver`] with the given name. This will use the memory fd backend + /// on Linux and macOS, which is compatible with forkserver. + pub fn new(name: Cow<'static, str>) -> Result { + Ok(Self { + name, output: None, + file: Self::file()?, + phantom: PhantomData, + }) + } + + /// Create a new `OutputObserver` with the given name. This use portable piped backend, which + /// only works with [`std::process::Command`]. + pub fn new_piped(name: Cow<'static, str>) -> Result { + Ok(Self { + name, + output: None, + file: None, + phantom: PhantomData, + }) + } + + /// Create a new `OutputObserver` with given name and file. + /// Useful for targets like nyx which writes to the same file again and again. + #[must_use] + pub fn new_file(name: Cow<'static, str>, file: File) -> Self { + Self { + name, + output: None, + file: Some(file), phantom: PhantomData, } } /// React to new stream data - pub fn observe(&mut self, data: &[u8]) { - self.output = Some(data.into()); + pub fn observe(&mut self, data: Vec) { + self.output = Some(data); + } + + #[must_use] + /// Return the raw fd, if any + pub fn as_raw_fd(&self) -> Option { + #[cfg(target_family = "unix")] + return self.file.as_ref().map(std::os::fd::AsRawFd::as_raw_fd); + #[cfg(not(target_family = "unix"))] + return None; } } @@ -211,16 +290,53 @@ impl Named for OutputObserver { } } -impl Observer for OutputObserver { +impl Observer for OutputObserver +where + T: 'static, +{ fn pre_exec_child(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { + if let Some(file) = self.file.as_mut() { + file.seek(SeekFrom::Start(0))?; + } self.output = None; Ok(()) } fn pre_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { - self.output = None; + self.pre_exec_child(_state, _input) + } + + fn post_exec_child( + &mut self, + _state: &mut S, + _input: &I, + _exit_kind: &crate::executors::ExitKind, + ) -> Result<(), Error> { + if let Some(file) = self.file.as_mut() { + if self.output.is_none() { + let pos = file.stream_position()?; + + if pos != 0 { + file.seek(SeekFrom::Start(0))?; + + let mut buf = vec![0; pos as usize]; + file.read_exact(&mut buf)?; + + self.observe(buf); + } + } + } Ok(()) } + + fn post_exec( + &mut self, + _state: &mut S, + _input: &I, + _exit_kind: &crate::executors::ExitKind, + ) -> Result<(), Error> { + self.post_exec_child(_state, _input, _exit_kind) + } } /// An observer that captures stdout of a target. diff --git a/libafl_bolts/src/argparse.rs b/libafl_bolts/src/argparse.rs index e5c021235f..cedbb81c47 100644 --- a/libafl_bolts/src/argparse.rs +++ b/libafl_bolts/src/argparse.rs @@ -4,50 +4,23 @@ use core::{ ffi::{c_char, c_int}, pin::Pin, }; -use std::{ffi::OsString, os::unix::ffi::OsStrExt}; +use std::os::unix::ffi::OsStrExt; -use crate::{Error, InputLocation, TargetArgs}; +use crate::{Error, StdTargetArgs, StdTargetArgsInner}; /// For creating an C-compatible argument #[derive(Debug)] pub struct CMainArgsBuilder { - program: Option, - input_location: InputLocation, - envs: Vec<(OsString, OsString)>, - args: Vec, + inner: StdTargetArgsInner, } -impl TargetArgs for CMainArgsBuilder { - fn arguments_ref(&self) -> &Vec { - &self.args +impl StdTargetArgs for CMainArgsBuilder { + fn inner(&self) -> &StdTargetArgsInner { + &self.inner } - fn arguments_mut(&mut self) -> &mut Vec { - &mut self.args - } - - fn input_location_ref(&self) -> &InputLocation { - &self.input_location - } - - fn input_location_mut(&mut self) -> &mut InputLocation { - &mut self.input_location - } - - fn envs_ref(&self) -> &Vec<(OsString, OsString)> { - &self.envs - } - - fn envs_mut(&mut self) -> &mut Vec<(OsString, OsString)> { - &mut self.envs - } - - fn program_ref(&self) -> &Option { - &self.program - } - - fn program_mut(&mut self) -> &mut Option { - &mut self.program + fn inner_mut(&mut self) -> &mut StdTargetArgsInner { + &mut self.inner } } @@ -62,10 +35,7 @@ impl CMainArgsBuilder { #[must_use] pub fn new() -> Self { Self { - program: None, - input_location: InputLocation::StdIn, - envs: Vec::new(), - args: Vec::new(), + inner: StdTargetArgsInner::default(), } } @@ -73,13 +43,13 @@ impl CMainArgsBuilder { pub fn build(&self) -> Result { let mut argv: Vec>> = Vec::new(); - if let Some(program) = &self.program { + if let Some(program) = &self.inner().program { argv.push(Box::pin(CString::new(program.as_bytes()).unwrap())); } else { return Err(Error::illegal_argument("Program not specified")); } - for args in &self.args { + for args in &self.inner().arguments { argv.push(Box::pin(CString::new(args.as_bytes()).unwrap())); } diff --git a/libafl_bolts/src/shmem.rs b/libafl_bolts/src/shmem.rs index 6f3b351ee5..c4e00bf8d8 100644 --- a/libafl_bolts/src/shmem.rs +++ b/libafl_bolts/src/shmem.rs @@ -1390,7 +1390,7 @@ pub mod unix_shmem { ops::{Deref, DerefMut}, ptr, slice, }; - use std::os::fd::IntoRawFd; + use std::{fs::File, os::fd::IntoRawFd}; use libc::{MAP_SHARED, PROT_READ, PROT_WRITE, close, fstat, ftruncate, mmap, munmap}; use nix::sys::memfd::{MemFdCreateFlag, memfd_create}; @@ -1533,6 +1533,21 @@ pub mod unix_shmem { } } + /// Dedicated Implementation to yield a [`std::fs::File`] + #[cfg(unix)] + impl MemfdShMemProvider { + /// Unlike [`MemfdShMemProvider::new`], this returns a file instead, without any mmap and truncate. + /// By default, the file size is capped by the tmpfs installed by the operating system, which is big + /// enough to hold all output and avoid spurious read/write errors from children. However, you are free + /// to set the size via [`std::fs::File::set_len`] + pub fn new_file() -> Result { + Ok(File::from(memfd_create( + c"libafl_file", + MemFdCreateFlag::empty(), + )?)) + } + } + /// Implement [`ShMemProvider`] for [`MemfdShMemProvider`] #[cfg(unix)] impl ShMemProvider for MemfdShMemProvider { diff --git a/libafl_bolts/src/target_args.rs b/libafl_bolts/src/target_args.rs index ec0b2ebdbb..0837e66951 100644 --- a/libafl_bolts/src/target_args.rs +++ b/libafl_bolts/src/target_args.rs @@ -29,27 +29,26 @@ pub enum InputLocation { }, } +/// The shared inner structs of trait [`StdTargetArgs`] +#[derive(Debug, Clone, Default)] +pub struct StdTargetArgsInner { + /// Program arguments + pub arguments: Vec, + /// Program main program + pub program: Option, + /// Input location, might be stdin or file or cli arg + pub input_location: InputLocation, + /// Program environments + pub envs: Vec<(OsString, OsString)>, +} + /// The main implementation trait of afl style arguments handling -pub trait TargetArgs: Sized { - /// Gets the arguments - fn arguments_ref(&self) -> &Vec; - /// Gets the mutable arguments - fn arguments_mut(&mut self) -> &mut Vec; +pub trait StdTargetArgs: Sized { + /// Get inner common arguments + fn inner(&self) -> &StdTargetArgsInner; - /// Gets the main program - fn program_ref(&self) -> &Option; - /// Gets the mutable main program - fn program_mut(&mut self) -> &mut Option; - - /// Gets the input file - fn input_location_ref(&self) -> &InputLocation; - /// Gets the mutable input file - fn input_location_mut(&mut self) -> &mut InputLocation; - - /// Get the environments - fn envs_ref(&self) -> &Vec<(OsString, OsString)>; - /// Get the mutable environments - fn envs_mut(&mut self) -> &mut Vec<(OsString, OsString)>; + /// Get mutable inner common arguments + fn inner_mut(&mut self) -> &mut StdTargetArgsInner; /// Adds an environmental var to the harness's commandline #[must_use] @@ -58,7 +57,8 @@ pub trait TargetArgs: Sized { K: AsRef, V: AsRef, { - self.envs_mut() + self.inner_mut() + .envs .push((key.as_ref().to_owned(), val.as_ref().to_owned())); self } @@ -75,20 +75,20 @@ pub trait TargetArgs: Sized { for (ref key, ref val) in vars { res.push((key.as_ref().to_owned(), val.as_ref().to_owned())); } - self.envs_mut().append(&mut res); + self.inner_mut().envs.append(&mut res); self } /// If use stdin #[must_use] fn use_stdin(&self) -> bool { - matches!(self.input_location_ref(), InputLocation::StdIn) + matches!(self.inner().input_location, InputLocation::StdIn) } /// Set input #[must_use] fn input(mut self, input: InputLocation) -> Self { - *self.input_location_mut() = input; + self.inner_mut().input_location = input; self } @@ -97,7 +97,7 @@ pub trait TargetArgs: Sized { /// Use [`Self::arg_input_file_std`] if you want to provide the input as a file instead. #[must_use] fn arg_input_arg(mut self) -> Self { - let argnum = self.arguments_ref().len(); + let argnum = self.inner().arguments.len(); self = self.input(InputLocation::Arg { argnum }); // Placeholder arg that gets replaced with the input name later. self = self.arg("PLACEHOLDER"); @@ -112,7 +112,7 @@ pub trait TargetArgs: Sized { fn arg_input_file>(self, path: P) -> Self { let mut moved = self.arg(path.as_ref()); assert!( - match moved.input_location_ref() { + match &moved.inner().input_location { InputLocation::File { out_file } => out_file.path.as_path() == path.as_ref(), InputLocation::StdIn => true, InputLocation::Arg { argnum: _ } => false, @@ -137,7 +137,7 @@ pub trait TargetArgs: Sized { where O: AsRef, { - *self.program_mut() = Some(program.as_ref().to_owned()); + self.inner_mut().program = Some(program.as_ref().to_owned()); self } @@ -150,7 +150,7 @@ pub trait TargetArgs: Sized { where O: AsRef, { - self.arguments_mut().push(arg.as_ref().to_owned()); + self.inner_mut().arguments.push(arg.as_ref().to_owned()); self } @@ -168,7 +168,7 @@ pub trait TargetArgs: Sized { for arg in args { res.push(arg.as_ref().to_owned()); } - self.arguments_mut().append(&mut res); + self.inner_mut().arguments.append(&mut res); self } @@ -189,7 +189,7 @@ pub trait TargetArgs: Sized { let mut moved = self; let mut use_arg_0_as_program = false; - if moved.program_ref().is_none() { + if moved.inner().program.is_none() { use_arg_0_as_program = true; } @@ -200,7 +200,7 @@ pub trait TargetArgs: Sized { // subsequent arguments as regular arguments use_arg_0_as_program = false; } else if item.as_ref() == "@@" { - match moved.input_location_ref().clone() { + match moved.inner().input_location.clone() { InputLocation::File { out_file } => { // If the input file name has been modified, use this one moved = moved.arg_input_file(&out_file.path); diff --git a/libafl_nyx/src/executor.rs b/libafl_nyx/src/executor.rs index 9d0ead07ba..6c141bef1f 100644 --- a/libafl_nyx/src/executor.rs +++ b/libafl_nyx/src/executor.rs @@ -1,6 +1,7 @@ use core::marker::PhantomData; use std::{ io::{Read, Seek}, + ops::IndexMut, os::fd::AsRawFd, }; @@ -11,7 +12,10 @@ use libafl::{ observers::{ObserversTuple, StdOutObserver}, state::HasExecutions, }; -use libafl_bolts::{AsSlice, tuples::RefIndexable}; +use libafl_bolts::{ + AsSlice, + tuples::{Handle, RefIndexable}, +}; use libnyx::NyxReturnValue; use crate::{cmplog::CMPLOG_ENABLED, helper::NyxHelper}; @@ -21,7 +25,7 @@ pub struct NyxExecutor { /// implement nyx function pub helper: NyxHelper, /// stdout - stdout: Option, + stdout: Option>, /// stderr // stderr: Option, /// observers @@ -112,7 +116,7 @@ where } }; - if let Some(ob) = self.stdout.as_mut() { + if let Some(ob) = self.stdout.clone() { let mut stdout = Vec::new(); self.helper.nyx_stdout.rewind()?; self.helper @@ -120,7 +124,7 @@ where .read_to_end(&mut stdout) .map_err(|e| Error::illegal_state(format!("Failed to read Nyx stdout: {e}")))?; - ob.observe(&stdout); + self.observers_mut().index_mut(&ob).observe(stdout); } unsafe { @@ -169,7 +173,7 @@ impl NyxExecutor { } pub struct NyxExecutorBuilder { - stdout: Option, + stdout: Option>, // stderr: Option, } @@ -188,7 +192,7 @@ impl NyxExecutorBuilder { } } - pub fn stdout(&mut self, stdout: StdOutObserver) -> &mut Self { + pub fn stdout(&mut self, stdout: Handle) -> &mut Self { self.stdout = Some(stdout); self } diff --git a/libafl_sugar/src/forkserver.rs b/libafl_sugar/src/forkserver.rs index 6dbe4a51cd..1aab06c6dc 100644 --- a/libafl_sugar/src/forkserver.rs +++ b/libafl_sugar/src/forkserver.rs @@ -7,7 +7,10 @@ use libafl::{ Error, HasMetadata, corpus::{CachedOnDiskCorpus, Corpus, OnDiskCorpus}, events::{EventConfig, LlmpRestartingEventManager, launcher::Launcher}, - executors::forkserver::{ForkserverExecutor, SHM_CMPLOG_ENV_VAR}, + executors::{ + StdChildArgs, + forkserver::{ForkserverExecutor, SHM_CMPLOG_ENV_VAR}, + }, feedback_and_fast, feedback_or, feedback_or_fast, feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, @@ -28,7 +31,7 @@ use libafl::{ state::{HasCorpus, StdState}, }; use libafl_bolts::{ - AsSliceMut, TargetArgs, + AsSliceMut, StdTargetArgs, core_affinity::Cores, nonzero, ownedref::OwnedRefMut, diff --git a/libafl_sugar/src/qemu.rs b/libafl_sugar/src/qemu.rs index 1020599f8f..dee302788e 100644 --- a/libafl_sugar/src/qemu.rs +++ b/libafl_sugar/src/qemu.rs @@ -131,11 +131,11 @@ where let mut out_dir = self.output_dir.clone(); if fs::create_dir(&out_dir).is_err() { - log::info!("Out dir at {:?} already exists.", &out_dir); + log::info!("Out dir at {} already exists.", &out_dir.display()); assert!( out_dir.is_dir(), - "Out dir at {:?} is not a valid directory!", - &out_dir + "Out dir at {} is not a valid directory!", + &out_dir.display() ); } let mut crashes = out_dir.clone(); diff --git a/utils/gdb_qemu/gdb_qemu/src/child.rs b/utils/gdb_qemu/gdb_qemu/src/child.rs index cd2a64f492..f91189ebef 100644 --- a/utils/gdb_qemu/gdb_qemu/src/child.rs +++ b/utils/gdb_qemu/gdb_qemu/src/child.rs @@ -6,7 +6,7 @@ use std::{ use anyhow::{anyhow, Result}; use nix::unistd::{dup2, execvp}; -use crate::{args::ChildArgs, exit::Exit}; +use crate::{args::StdChildArgs, exit::Exit}; pub struct Child { argv: Vec, @@ -47,7 +47,7 @@ impl Child { Ok(()) } - pub fn new(args: &impl ChildArgs, fd1: RawFd, fd2: RawFd) -> Child { + pub fn new(args: &impl StdChildArgs, fd1: RawFd, fd2: RawFd) -> Child { Child { argv: args.argv().to_vec(), fd1,