mirror of
https://github.com/microsoft/onefuzz.git
synced 2025-06-21 21:54:26 +00:00
Add uninstrumented block coverage recording (#280)
This commit is contained in:
255
src/agent/Cargo.lock
generated
255
src/agent/Cargo.lock
generated
@ -16,10 +16,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e"
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.14"
|
||||
name = "adler32"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b476ce7103678b0c6d3d395dbbae31d48ff910bd28be979ba5d48c6351131d0d"
|
||||
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@ -35,9 +41,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.33"
|
||||
version = "1.0.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1fd36ffbb1fb7c834eac128ea8d0e310c5aeb635548f9d58861e1308d46e71c"
|
||||
checksum = "bf8dcb5b4bbaa28653b647d8c77bd4ed40183b48882e130c1f1ffb73de069fd7"
|
||||
|
||||
[[package]]
|
||||
name = "appinsights"
|
||||
@ -98,9 +104,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "async-io"
|
||||
version = "1.1.10"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d54bc4c1c7292475efb2253227dbcfad8fe1ca4c02bc62c510cc2f3da5c4704e"
|
||||
checksum = "40a0b2bb8ae20fede194e779150fe283f65a4a08461b496de546ec366b174ad9"
|
||||
dependencies = [
|
||||
"concurrent-queue",
|
||||
"fastrand",
|
||||
@ -127,15 +133,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "async-std"
|
||||
version = "1.6.5"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9fa76751505e8df1c7a77762f60486f60c71bbd9b8557f4da6ad47d083732ed"
|
||||
checksum = "a7e82538bc65a25dbdff70e4c5439d52f068048ab97cdea0acd73f131594caa1"
|
||||
dependencies = [
|
||||
"async-global-executor",
|
||||
"async-io",
|
||||
"async-mutex",
|
||||
"blocking",
|
||||
"crossbeam-utils 0.7.2",
|
||||
"crossbeam-utils 0.8.0",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
@ -299,9 +305,9 @@ checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.61"
|
||||
version = "1.0.62"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed67cbde08356238e75fc4656be4749481eeffb09e19f320a25237d5221c985d"
|
||||
checksum = "f1770ced377336a88a67c473594ccc14eca6f4559217c34f64aac8f83d641b40"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
@ -354,15 +360,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "const_fn"
|
||||
version = "0.4.2"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce90df4c658c62f12d78f7508cf92f9173e5184a539c10bfe54a3107b3ffd0f2"
|
||||
checksum = "c478836e029dcef17fb47c89023448c64f781a046e0300e257ad8225ae59afab"
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.7.0"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171"
|
||||
checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
@ -370,9 +376,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.7.0"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac"
|
||||
checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b"
|
||||
|
||||
[[package]]
|
||||
name = "coverage"
|
||||
@ -380,12 +386,17 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
"debugger",
|
||||
"env_logger 0.8.1",
|
||||
"fixedbitset",
|
||||
"goblin",
|
||||
"iced-x86",
|
||||
"log",
|
||||
"memmap",
|
||||
"object",
|
||||
"pdb",
|
||||
"pete",
|
||||
"procfs",
|
||||
"serde",
|
||||
"uuid",
|
||||
"winapi 0.3.9",
|
||||
@ -407,6 +418,15 @@ version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634"
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.4.4"
|
||||
@ -555,7 +575,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"humantime",
|
||||
"humantime 1.3.0",
|
||||
"log",
|
||||
"regex",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54532e3223c5af90a6a757c90b5c5521564b07e5e7a958681bcd2afad421cdcd"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"humantime 2.0.1",
|
||||
"log",
|
||||
"regex",
|
||||
"termcolor",
|
||||
@ -617,11 +650,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "filetime"
|
||||
version = "0.2.12"
|
||||
version = "0.2.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ed85775dcc68644b5c950ac06a2b23768d3bc9390464151aaf27136998dcf9e"
|
||||
checksum = "0c122a393ea57648015bf06fbd3d372378992e86b9ff5a7a497b076a28c79efe"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10",
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"winapi 0.3.9",
|
||||
@ -633,6 +666,18 @@ version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e08c8bc7575d7e091fe0706963bd22e2a4be6a64da995f03b2a5a57d66ad015"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7411863d55df97a419aa64cb4d2f167103ea9d767e2c54a1868b7ac3f6b47129"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"crc32fast",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
@ -681,6 +726,16 @@ version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7684cf33bb7f28497939e8c7cf17e3e4e3b8d9a0080ffa4f8ae2f515442ee855"
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ece68d15c92e84fa4f19d3780f1294e5ca82a78a6d515f1efaabcc144688be00"
|
||||
dependencies = [
|
||||
"matches",
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fsevent"
|
||||
version = "0.4.0"
|
||||
@ -718,9 +773,9 @@ checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.7"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95314d38584ffbfda215621d723e0a3906f032e03ae5551e650058dac83d4797"
|
||||
checksum = "9b3b0c040a1fe6529d30b3c5944b280c7f0dcb2930d2c3062bca967b602583d0"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
@ -733,9 +788,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.7"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0448174b01148032eed37ac4aed28963aaaa8cfa93569a08e5b479bbc6c2c151"
|
||||
checksum = "4b7109687aa4e177ef6fe84553af6280ef2778bdb7783ba44c9dc3399110fe64"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
@ -743,15 +798,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.7"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "18eaa56102984bed2c88ea39026cff3ce3b4c7f508ca970cedf2450ea10d4e46"
|
||||
checksum = "847ce131b72ffb13b6109a221da9ad97a64cbe48feb1028356b836b47b8f1748"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.7"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f5f8e0c9258abaea85e78ebdda17ef9666d390e987f006be6080dfe354b708cb"
|
||||
checksum = "4caa2b2b68b880003057c1dd49f1ed937e38f22fcf6c212188a121f08cf40a65"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
@ -760,9 +815,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.7"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e1798854a4727ff944a7b12aa999f58ce7aa81db80d2dfaaf2ba06f065ddd2b"
|
||||
checksum = "611834ce18aaa1bd13c4b374f5d653e1027cf99b6b502584ff8c9a64413b30bb"
|
||||
|
||||
[[package]]
|
||||
name = "futures-lite"
|
||||
@ -781,9 +836,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.7"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e36fccf3fc58563b4a14d265027c627c3b665d7fed489427e88e7cc929559efe"
|
||||
checksum = "77408a692f1f97bcc61dc001d752e00643408fbc922e4d634c655df50d595556"
|
||||
dependencies = [
|
||||
"proc-macro-hack",
|
||||
"proc-macro2 1.0.24",
|
||||
@ -793,24 +848,24 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.7"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e3ca3f17d6e8804ae5d3df7a7d35b2b3a6fe89dac84b31872720fc3060a0b11"
|
||||
checksum = "f878195a49cee50e006b02b93cf7e0a95a38ac7b776b4c4d9cc1207cd20fcb3d"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.7"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96d502af37186c4fef99453df03e374683f8a1eec9dcc1e66b3b82dc8278ce3c"
|
||||
checksum = "7c554eb5bf48b2426c4771ab68c6b14468b6e76cc90996f528c3338d761a4d0d"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.7"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abcb44342f62e6f3e8ac427b8aa815f724fd705dfad060b18ac7866c15bb8e34"
|
||||
checksum = "d304cff4a7b99cfb7986f7d43fbe93d175e72e704a8860787cc95e9ffd85cbd2"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
@ -987,10 +1042,16 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.13.8"
|
||||
name = "humantime"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f3afcfae8af5ad0576a31e768415edb627824129e8e5a29b8bfccb2f234e835"
|
||||
checksum = "3c1ad908cc71012b7bea4d0c53ba96a8cba9962f048fa68d143376143d863b7a"
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.13.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6ad767baac13b44d4529fcf58ba2cd0995e36e7b435bc5b039de6f47e880dbf"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
@ -1002,7 +1063,7 @@ dependencies = [
|
||||
"httparse",
|
||||
"httpdate",
|
||||
"itoa",
|
||||
"pin-project 0.4.27",
|
||||
"pin-project 1.0.1",
|
||||
"socket2",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
@ -1068,9 +1129,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "inotify-sys"
|
||||
version = "0.1.3"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e74a1aa87c59aeff6ef2cc2fa62d41bc43f54952f55652656b18a02fd5e356c0"
|
||||
checksum = "c4563555856585ab3180a5bf0b2f9f8d301a728462afffc8195b3f5394229c55"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
@ -1169,6 +1230,24 @@ version = "0.2.80"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614"
|
||||
|
||||
[[package]]
|
||||
name = "libflate"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "389de7875e06476365974da3e7ff85d55f1972188ccd9f6020dd7c8156e17914"
|
||||
dependencies = [
|
||||
"adler32",
|
||||
"crc32fast",
|
||||
"libflate_lz77",
|
||||
"rle-decode-fast",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libflate_lz77"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3286f09f7d4926fc486334f28d8d2e6ebe4f7f9994494b6dab27ddfad2c9b11b"
|
||||
|
||||
[[package]]
|
||||
name = "libproc"
|
||||
version = "0.3.2"
|
||||
@ -1344,9 +1423,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "native-tls"
|
||||
version = "0.2.4"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b0d88c06fe90d5ee94048ba40409ef1d9315d86f6f38c2efdaad4fb50c58b2d"
|
||||
checksum = "1a1cda389c26d6b88f3d2dc38aa1b750fe87d298cc5d795ec9e975f402f00372"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"libc",
|
||||
@ -1467,6 +1546,10 @@ name = "object"
|
||||
version = "0.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d3b63360ec3cb337817c2dbd47ab4a0f170d285d8e5a2064600f3def1402397"
|
||||
dependencies = [
|
||||
"flate2",
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
@ -1526,7 +1609,7 @@ dependencies = [
|
||||
"appinsights",
|
||||
"async-trait",
|
||||
"clap",
|
||||
"env_logger",
|
||||
"env_logger 0.7.1",
|
||||
"futures",
|
||||
"hex",
|
||||
"lazy_static",
|
||||
@ -1554,7 +1637,7 @@ dependencies = [
|
||||
"async-trait",
|
||||
"clap",
|
||||
"downcast-rs",
|
||||
"env_logger",
|
||||
"env_logger 0.7.1",
|
||||
"futures",
|
||||
"log",
|
||||
"onefuzz",
|
||||
@ -1750,9 +1833,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.9"
|
||||
version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20"
|
||||
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
@ -1832,6 +1915,21 @@ dependencies = [
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "procfs"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4a336c8310f4955f343935b9c11a30254d1ad8fad98ec257a4407a061a6fd49"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"byteorder",
|
||||
"chrono",
|
||||
"hex",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"libflate",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-error"
|
||||
version = "1.2.3"
|
||||
@ -2018,10 +2116,16 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rstack"
|
||||
version = "0.3.1"
|
||||
name = "rle-decode-fast"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85dc9d336d64dd0210b24a981da1a972801f1b262d894c4eb2feeed72fd3d774"
|
||||
checksum = "cabe4fa914dec5870285fa7f71f602645da47c486e68486d2b4ceb4a343e90ac"
|
||||
|
||||
[[package]]
|
||||
name = "rstack"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5db35178d78a6c680852a6b48b089100d038c82fde25283a77ee923c3f6f90bd"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10",
|
||||
"libc",
|
||||
@ -2097,9 +2201,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "0.4.4"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64808902d7d99f78eaddd2b4e2509713babc3dc3c85ad6f4c447680f3c01e535"
|
||||
checksum = "c1759c2e3c8580017a484a7ac56d3abc5a6c1feadf88db2f3633f12ae4268c69"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"core-foundation",
|
||||
@ -2110,9 +2214,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "security-framework-sys"
|
||||
version = "0.4.3"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17bf11d99252f512695eb468de5516e5cf75455521e69dfe343f3b74e4748405"
|
||||
checksum = "f99b9d5e26d2a71633cc4f2ebae7cc9f874044e0c351a27e17892d76dce5678b"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
@ -2190,12 +2294,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.9.1"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2933378ddfeda7ea26f48c555bdad8bb446bf8a3d17832dc83e380d444cfb8c1"
|
||||
checksum = "6e7aab86fe2149bad8c507606bdb3f4ef5e7b2380eb92350f56122cca72a42a8"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"cfg-if 0.1.10",
|
||||
"cfg-if 1.0.0",
|
||||
"cpuid-bool",
|
||||
"digest",
|
||||
"opaque-debug",
|
||||
@ -2408,18 +2512,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.21"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "318234ffa22e0920fe9a40d7b8369b5f649d490980cf7aadcf1eb91594869b42"
|
||||
checksum = "0e9ae34b84616eedaaf1e9dd6026dbe00dcafa92aa0c8077cb69df1fcfe5e53e"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.21"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cae2447b6282786c3493999f40a9be2a6ad20cb8bd268b0a0dbf5a065535c0ab"
|
||||
checksum = "9ba20f23e85b10754cd195504aebf6a27e2e6cbe28c17778a0c930724628dd56"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.24",
|
||||
"quote 1.0.7",
|
||||
@ -2620,9 +2724,9 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
|
||||
|
||||
[[package]]
|
||||
name = "unwind"
|
||||
version = "0.3.0"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89784c377152a6b8a4ecdf1e2a0fc15345773a552c1752ffd2365149aa8d79ca"
|
||||
checksum = "b737c38ba258c25916dd4002b12e631b180b6ea63528147a94d6364f68d886df"
|
||||
dependencies = [
|
||||
"foreign-types 0.5.0",
|
||||
"libc",
|
||||
@ -2631,9 +2735,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unwind-sys"
|
||||
version = "0.1.1"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd1c4a6d1cfe0072924d1b1d4ca6faa211c95056666979d7ef1bab4cd206057f"
|
||||
checksum = "e72a873f034625fa57b8c2c16e14f2a44dc93c08fe330e81405b72d0aa42b2ff"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"pkg-config",
|
||||
@ -2641,10 +2745,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.1.1"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb"
|
||||
checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
"matches",
|
||||
"percent-encoding",
|
||||
@ -2805,6 +2910,12 @@ version = "0.2.68"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d649a3145108d7d3fbcde896a468d1bd636791823c9921135218ad89be08307"
|
||||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.57.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32fddd575d477c6e9702484139cf9f23dcd554b06d185ed0f56c857dd3a47aa6"
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.45"
|
||||
|
@ -1,6 +1,7 @@
|
||||
[workspace]
|
||||
members = [
|
||||
"atexit",
|
||||
"coverage",
|
||||
"debugger",
|
||||
"input-tester",
|
||||
"onefuzz",
|
||||
|
@ -8,6 +8,7 @@ edition = "2018"
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
bincode = "1.3"
|
||||
debugger = { path = "../debugger" }
|
||||
fixedbitset = "0.3"
|
||||
goblin = "0.2"
|
||||
iced-x86 = { version = "1.1", features = ["decoder", "op_code_info", "instr_info", "masm"] }
|
||||
@ -16,6 +17,14 @@ memmap = "0.7"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
uuid = { version = "0.8", features = ["guid"] }
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
pdb = "0.6"
|
||||
winapi = "0.3"
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
object = "0.22"
|
||||
pete = "0.3"
|
||||
procfs = "0.8"
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = "0.8"
|
68
src/agent/coverage/examples/block_coverage.rs
Normal file
68
src/agent/coverage/examples/block_coverage.rs
Normal file
@ -0,0 +1,68 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
use std::env;
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn main() -> Result<()> {
|
||||
use std::process::Command;
|
||||
|
||||
env_logger::init();
|
||||
|
||||
let mut args = env::args().skip(1);
|
||||
let exe = args.next().unwrap();
|
||||
let args: Vec<_> = args.collect();
|
||||
|
||||
let mut cmd = Command::new(exe);
|
||||
cmd.args(&args);
|
||||
|
||||
let coverage = coverage::block::windows::record(cmd)?;
|
||||
let hit = coverage.count_blocks_hit();
|
||||
let found = coverage.count_blocks();
|
||||
let percent = 100.0 * (hit as f64) / (found as f64);
|
||||
|
||||
log::info!("block coverage = {}/{} ({:.2}%)", hit, found, percent);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn main() -> Result<()> {
|
||||
use coverage::block::linux::record;
|
||||
use pete::Command;
|
||||
|
||||
env_logger::init();
|
||||
|
||||
let argv = env::args().skip(1).collect();
|
||||
let cmd = Command::new(argv)?;
|
||||
|
||||
let coverage = record(cmd)?;
|
||||
|
||||
for m in coverage.modules.values() {
|
||||
let mut hit = 0;
|
||||
let mut found = 0;
|
||||
|
||||
let name = m.module.file_name().unwrap().to_string_lossy();
|
||||
|
||||
log::info!("{}", m.module.display());
|
||||
|
||||
for b in m.blocks.values() {
|
||||
found += 1;
|
||||
|
||||
if b.count > 0 {
|
||||
hit += 1;
|
||||
};
|
||||
|
||||
let marker = if b.count == 0 { " " } else { "x" };
|
||||
|
||||
log::debug!(" [{}] {}+{:x}", marker, name, b.offset);
|
||||
}
|
||||
|
||||
let percent = 100.0 * (hit as f64) / (found as f64);
|
||||
log::info!("block coverage = {}/{} ({:.2}%)", hit, found, percent);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
59
src/agent/coverage/src/block.rs
Normal file
59
src/agent/coverage/src/block.rs
Normal file
@ -0,0 +1,59 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub mod linux;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub mod windows;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// Block coverage for a module.
|
||||
///
|
||||
/// May describe coverage relative to a single input or corpus of inputs.
|
||||
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub struct ModuleCov {
|
||||
/// Absolute path to the module on the filesystem where coverage was recorded.
|
||||
pub module: PathBuf,
|
||||
|
||||
/// Mapping from basic block module-relative offsets to block coverage info.
|
||||
pub blocks: BTreeMap<u64, BlockCov>,
|
||||
}
|
||||
|
||||
impl ModuleCov {
|
||||
pub fn new(module: impl Into<PathBuf>, blocks: impl IntoIterator<Item = u64>) -> Self {
|
||||
let module = module.into();
|
||||
let blocks = blocks.into_iter().map(|o| (o, BlockCov::new(o))).collect();
|
||||
|
||||
Self { module, blocks }
|
||||
}
|
||||
}
|
||||
|
||||
/// Coverage info for a specific block, identified by its offset.
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub struct BlockCov {
|
||||
/// Offset of the block, relative to the module base load address.
|
||||
pub offset: u64,
|
||||
|
||||
/// Number of times a block was seen to be executed, relative to some input
|
||||
/// or corpus.
|
||||
///
|
||||
/// Right now, we only set one-shot breakpoints, so the max `count` for a
|
||||
/// single input is 1. In this usage, if we measure corpus block coverage
|
||||
/// with `sum()` as the aggregation function, then `count` / `corpus.len()`
|
||||
/// tells us the proportion of corpus inputs that cover a block.
|
||||
///
|
||||
/// If we reset breakpoints and recorded multiple block hits per input, then
|
||||
/// the corpus semantics would depend on the aggregation function.
|
||||
pub count: u32,
|
||||
}
|
||||
|
||||
impl BlockCov {
|
||||
pub fn new(offset: u64) -> Self {
|
||||
Self { offset, count: 0 }
|
||||
}
|
||||
}
|
486
src/agent/coverage/src/block/linux.rs
Normal file
486
src/agent/coverage/src/block/linux.rs
Normal file
@ -0,0 +1,486 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::ffi::OsStr;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::{format_err, Result};
|
||||
use iced_x86::{Decoder, DecoderOptions, Instruction};
|
||||
use object::endian::LittleEndian as LE;
|
||||
use object::{read::elf, Object, ObjectSection, ObjectSegment, ObjectSymbol};
|
||||
use pete::{Command, Ptracer, Restart, Signal, Stop, Tracee};
|
||||
use procfs::process::{MMapPath, MemoryMap, Process};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::block::ModuleCov;
|
||||
|
||||
pub fn record(cmd: Command) -> Result<CommandBlockCov> {
|
||||
let mut recorder = Recorder::default();
|
||||
recorder.record(cmd)?;
|
||||
Ok(recorder.coverage)
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct Recorder {
|
||||
breakpoints: Breakpoints,
|
||||
coverage: CommandBlockCov,
|
||||
images: Option<Images>,
|
||||
}
|
||||
|
||||
impl Recorder {
|
||||
pub fn record(&mut self, cmd: Command) -> Result<()> {
|
||||
use pete::ptracer::Options;
|
||||
|
||||
let mut ptracer = Ptracer::new();
|
||||
|
||||
// Attach stop.
|
||||
let mut tracee = ptracer.spawn(cmd)?;
|
||||
|
||||
ptracer.restart(tracee, Restart::Continue)?;
|
||||
|
||||
// Continue until `exec()`.
|
||||
while let Some(t) = ptracer.wait()? {
|
||||
if let Stop::Exec(..) = t.stop {
|
||||
tracee = t;
|
||||
break;
|
||||
}
|
||||
|
||||
ptracer.restart(tracee, Restart::Continue)?;
|
||||
}
|
||||
|
||||
// Do not follow forks.
|
||||
//
|
||||
// After this, we assume that any new tracee is a thread in the same
|
||||
// group as the root tracee.
|
||||
let mut options = Options::all();
|
||||
options.remove(Options::PTRACE_O_TRACEFORK);
|
||||
options.remove(Options::PTRACE_O_TRACEVFORK);
|
||||
options.remove(Options::PTRACE_O_TRACEEXEC);
|
||||
tracee.set_options(options)?;
|
||||
|
||||
self.images = Some(Images::new(tracee.pid.as_raw()));
|
||||
self.update_images(&mut tracee)?;
|
||||
|
||||
ptracer.restart(tracee, Restart::Syscall)?;
|
||||
|
||||
while let Some(mut tracee) = ptracer.wait()? {
|
||||
match tracee.stop {
|
||||
Stop::SyscallEnterStop(..) => log::trace!("syscall-enter: {:?}", tracee.stop),
|
||||
Stop::SyscallExitStop(..) => {
|
||||
self.update_images(&mut tracee)?;
|
||||
}
|
||||
Stop::SignalDeliveryStop(_pid, Signal::SIGTRAP) => {
|
||||
self.on_breakpoint(&mut tracee)?;
|
||||
}
|
||||
Stop::Clone(pid, tid) => {
|
||||
// Only seen when the `VM_CLONE` flag is set, as of Linux 4.15.
|
||||
log::info!("new thread: {} -> {}", pid, tid);
|
||||
}
|
||||
_ => {
|
||||
log::debug!("stop: {:?}", tracee.stop);
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(err) = ptracer.restart(tracee, Restart::Syscall) {
|
||||
log::error!("unable to restart tracee: {}", err);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_images(&mut self, tracee: &mut Tracee) -> Result<()> {
|
||||
let images = self
|
||||
.images
|
||||
.as_mut()
|
||||
.ok_or_else(|| format_err!("internal error: recorder images not initialized"))?;
|
||||
let events = images.update()?;
|
||||
|
||||
for (_base, image) in &events.loaded {
|
||||
self.on_module_load(tracee, image)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_breakpoint(&mut self, tracee: &mut Tracee) -> Result<()> {
|
||||
let mut regs = tracee.registers()?;
|
||||
|
||||
log::trace!("hit breakpoint: {:x} (~{})", regs.rip - 1, tracee.pid);
|
||||
|
||||
// Adjust for synthetic `int3`.
|
||||
let pc = regs.rip - 1;
|
||||
|
||||
if self.breakpoints.clear(tracee, pc)? {
|
||||
let images = self
|
||||
.images
|
||||
.as_ref()
|
||||
.ok_or_else(|| format_err!("internal error: recorder images not initialized"))?;
|
||||
let image = images
|
||||
.find_va_image(pc)
|
||||
.ok_or_else(|| format_err!("unable to find image for va = {:x}", pc))?;
|
||||
|
||||
self.coverage.increment(&image, pc)?;
|
||||
|
||||
// Execute clobbered instruction on restart.
|
||||
regs.rip = pc;
|
||||
tracee.set_registers(regs)?;
|
||||
} else {
|
||||
// Assume the tracee concurrently executed an `int3` that we restored
|
||||
// in another handler.
|
||||
//
|
||||
// We could improve on this by not removing breakpoints metadata when
|
||||
// clearing, but making their value a state.
|
||||
log::debug!("no breakpoint at {:x}, assuming race", pc);
|
||||
regs.rip = pc;
|
||||
tracee.set_registers(regs)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_module_load(&mut self, tracee: &mut Tracee, image: &ModuleImage) -> Result<()> {
|
||||
log::info!("module load: {}", image.path().display());
|
||||
|
||||
let blocks = find_module_blocks(image.path())?;
|
||||
|
||||
log::debug!("found {} blocks", blocks.len());
|
||||
|
||||
if blocks.is_empty() {
|
||||
// This almost certainly means the binary was stripped of symbols.
|
||||
log::warn!("no blocks for module, not setting breakpoints");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let new = self.coverage.add_module(image, &blocks)?;
|
||||
|
||||
if new {
|
||||
for b in blocks {
|
||||
let va = image.offset_to_va(b.offset);
|
||||
log::trace!("set breakpoint: {:x} (~{})", va, tracee.pid);
|
||||
self.breakpoints.set(tracee, va)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Block coverage for a command invocation.
|
||||
///
|
||||
/// Organized by module.
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub struct CommandBlockCov {
|
||||
pub modules: BTreeMap<PathBuf, ModuleCov>,
|
||||
}
|
||||
|
||||
impl CommandBlockCov {
|
||||
pub fn add_module(&mut self, image: &ModuleImage, blocks: &[Block]) -> Result<bool> {
|
||||
if self.modules.contains_key(image.path()) {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let blocks = blocks.iter().map(|b| b.offset);
|
||||
let cov = ModuleCov::new(image.path(), blocks);
|
||||
|
||||
self.modules.insert(image.path().to_owned(), cov);
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
pub fn increment(&mut self, image: &ModuleImage, va: u64) -> Result<()> {
|
||||
if let Some(cov) = self.modules.get_mut(image.path()) {
|
||||
let offset = image.va_to_offset(va);
|
||||
if let Some(block) = cov.blocks.get_mut(&offset) {
|
||||
block.count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Executable memory-mapped files for a process.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Images {
|
||||
mapped: BTreeMap<u64, ModuleImage>,
|
||||
pid: i32,
|
||||
}
|
||||
|
||||
impl Images {
|
||||
pub fn new(pid: i32) -> Self {
|
||||
let mapped = BTreeMap::default();
|
||||
|
||||
Self { mapped, pid }
|
||||
}
|
||||
|
||||
pub fn mapped(&self) -> impl Iterator<Item = (u64, &ModuleImage)> {
|
||||
self.mapped.iter().map(|(va, i)| (*va, i))
|
||||
}
|
||||
|
||||
pub fn update(&mut self) -> Result<LoadEvents> {
|
||||
let proc = Process::new(self.pid)?;
|
||||
|
||||
let mut new = BTreeMap::default();
|
||||
|
||||
for map in proc.maps()? {
|
||||
if let Ok(image) = ModuleImage::new(map) {
|
||||
new.insert(image.base(), image);
|
||||
}
|
||||
}
|
||||
|
||||
let events = LoadEvents::new(&self.mapped, &new);
|
||||
|
||||
self.mapped = new;
|
||||
|
||||
Ok(events)
|
||||
}
|
||||
|
||||
pub fn find_va_image(&self, va: u64) -> Option<&ModuleImage> {
|
||||
for (base, image) in self.mapped() {
|
||||
if va < base {
|
||||
continue;
|
||||
}
|
||||
|
||||
if image.region().contains(&va) {
|
||||
return Some(&image);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// A `MemoryMap` that is known to be file-backed and executable.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct ModuleImage {
|
||||
map: MemoryMap,
|
||||
}
|
||||
|
||||
impl ModuleImage {
|
||||
pub fn new(map: MemoryMap) -> Result<Self> {
|
||||
if let MMapPath::Path(..) = &map.pathname {
|
||||
if map.perms.contains("x") {
|
||||
return Ok(ModuleImage { map });
|
||||
} else {
|
||||
anyhow::bail!("memory mapping is not executable");
|
||||
}
|
||||
} else {
|
||||
anyhow::bail!("memory mapping is not file-backed");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &OsStr {
|
||||
// File name existence guaranteed by how we acquired the `Path`.
|
||||
self.path().file_name().unwrap()
|
||||
}
|
||||
|
||||
pub fn path(&self) -> &Path {
|
||||
if let MMapPath::Path(path) = &self.map.pathname {
|
||||
return &path;
|
||||
}
|
||||
|
||||
// Enforced by ctor.
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
pub fn map(&self) -> &MemoryMap {
|
||||
&self.map
|
||||
}
|
||||
|
||||
pub fn base(&self) -> u64 {
|
||||
self.map.address.0 - self.map.offset
|
||||
}
|
||||
|
||||
pub fn size(&self) -> u64 {
|
||||
self.map.address.1 - self.map.address.0
|
||||
}
|
||||
|
||||
pub fn region(&self) -> std::ops::Range<u64> {
|
||||
(self.map.address.0)..(self.map.address.1)
|
||||
}
|
||||
|
||||
pub fn va_to_offset(&self, va: u64) -> u64 {
|
||||
va - self.base()
|
||||
}
|
||||
|
||||
pub fn offset_to_va(&self, offset: u64) -> u64 {
|
||||
self.base() + offset
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LoadEvents {
|
||||
pub loaded: Vec<(u64, ModuleImage)>,
|
||||
pub unloaded: Vec<(u64, ModuleImage)>,
|
||||
}
|
||||
|
||||
impl LoadEvents {
|
||||
pub fn new(old: &BTreeMap<u64, ModuleImage>, new: &BTreeMap<u64, ModuleImage>) -> Self {
|
||||
// New not in old.
|
||||
let loaded: Vec<_> = new
|
||||
.iter()
|
||||
.filter(|(nva, n)| {
|
||||
old.iter()
|
||||
.find(|(iva, i)| nva == iva && n.path() == i.path())
|
||||
.is_none()
|
||||
})
|
||||
.map(|(va, i)| (*va, i.clone()))
|
||||
.collect();
|
||||
|
||||
// Old not in new.
|
||||
let unloaded: Vec<_> = old
|
||||
.iter()
|
||||
.filter(|(iva, i)| {
|
||||
new.iter()
|
||||
.find(|(nva, n)| nva == iva && n.path() == i.path())
|
||||
.is_none()
|
||||
})
|
||||
.map(|(va, i)| (*va, i.clone()))
|
||||
.collect();
|
||||
|
||||
Self { loaded, unloaded }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Breakpoints {
|
||||
saved: BTreeMap<u64, u8>,
|
||||
}
|
||||
|
||||
impl Breakpoints {
|
||||
pub fn set(&mut self, tracee: &mut Tracee, va: u64) -> Result<()> {
|
||||
// Return if the breakpoint exists. We don't want to conclude that the
|
||||
// saved instruction byte was `0xcc`.
|
||||
if self.saved.contains_key(&va) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut data = [0u8];
|
||||
tracee.read_memory_mut(va, &mut data)?;
|
||||
self.saved.insert(va, data[0]);
|
||||
tracee.write_memory(va, &[0xcc])?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn clear(&mut self, tracee: &mut Tracee, va: u64) -> Result<bool> {
|
||||
let data = self.saved.remove(&va);
|
||||
|
||||
let cleared = if let Some(data) = data {
|
||||
tracee.write_memory(va, &[data])?;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
Ok(cleared)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub struct Block {
|
||||
pub offset: u64,
|
||||
}
|
||||
|
||||
type ElfFile<'a> = elf::ElfFile64<'a, LE>;
|
||||
type ElfSymbol<'a> = elf::ElfSymbol64<'a, 'a, LE>;
|
||||
type ElfSection<'a> = elf::ElfSection64<'a, 'a, LE>;
|
||||
|
||||
pub fn find_module_blocks(module: &Path) -> Result<Vec<Block>> {
|
||||
let data = std::fs::read(module)?;
|
||||
let elf = ElfFile::parse(&data)?;
|
||||
|
||||
let load_va =
|
||||
elf.segments().map(|s| s.address()).min().ok_or_else(|| {
|
||||
format_err!("no loadable segments for ELF object ({})", module.display())
|
||||
})?;
|
||||
|
||||
let mut blocks = vec![];
|
||||
|
||||
for sym in elf.symbols() {
|
||||
if sym.kind() == object::SymbolKind::Text && sym.size() > 0 {
|
||||
if let Some(idx) = sym.section().index() {
|
||||
let section = elf.section_by_index(idx)?;
|
||||
let sym_blocks = find_symbol_blocks(section, sym)?;
|
||||
blocks.extend(sym_blocks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The blocks we've collected have VAs as `offset` values, assuming the preferred base
|
||||
// load address. Make them true offsets, relative to that image base address.
|
||||
for block in &mut blocks {
|
||||
block.offset -= load_va;
|
||||
}
|
||||
|
||||
Ok(blocks)
|
||||
}
|
||||
|
||||
pub fn find_symbol_blocks(section: ElfSection, sym: ElfSymbol) -> Result<Vec<Block>> {
|
||||
// Slice symbol's data within section.
|
||||
let lo = (sym.address() - section.address()) as usize;
|
||||
let hi = lo + (sym.size() as usize);
|
||||
let data = section.data()?;
|
||||
let data = &data[lo..hi];
|
||||
|
||||
let mut decoder = Decoder::new(64, data, DecoderOptions::NONE);
|
||||
decoder.set_ip(sym.address());
|
||||
|
||||
// Contains leaders with VAs, assuming section load address.
|
||||
let mut leaders = BTreeSet::new();
|
||||
|
||||
// Function entry is a leader.
|
||||
leaders.insert(sym.address());
|
||||
|
||||
let mut inst = Instruction::default();
|
||||
while decoder.can_decode() {
|
||||
decoder.decode_out(&mut inst);
|
||||
|
||||
if let Some((target, conditional)) = branch_target(&inst) {
|
||||
// The branch target is a leader.
|
||||
leaders.insert(target);
|
||||
|
||||
// Only mark the next instruction as a leader if the branch is conditional.
|
||||
// This will give an invalid basic block decomposition if the leaders we emit
|
||||
// are used as delimiters. In particular, blocks that end with a `jmp` will be
|
||||
// too large, and have an unconditional branch mid-block.
|
||||
//
|
||||
// However, we only care about the leaders as block entry points, so we can
|
||||
// set software breakpoints. These maybe-unreachable leaders are a liability
|
||||
// wrt mutating the running process' code, so we discard them for now.
|
||||
if conditional {
|
||||
// The next instruction is a leader, if it exists.
|
||||
if decoder.can_decode() {
|
||||
// We decoded the current instruction, so the decoder offset is
|
||||
// set to the next instruction.
|
||||
let next = decoder.ip() as u64;
|
||||
leaders.insert(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let blocks: Vec<_> = leaders.iter().map(|va| Block { offset: *va }).collect();
|
||||
|
||||
Ok(blocks)
|
||||
}
|
||||
|
||||
// Returns the virtual address of a branch target, if present, with a flag that
|
||||
// is true when the branch is conditional.
|
||||
fn branch_target(inst: &iced_x86::Instruction) -> Option<(u64, bool)> {
|
||||
use iced_x86::FlowControl;
|
||||
|
||||
match inst.flow_control() {
|
||||
FlowControl::ConditionalBranch => Some((inst.near_branch_target(), true)),
|
||||
FlowControl::UnconditionalBranch => Some((inst.near_branch_target(), false)),
|
||||
FlowControl::Call
|
||||
| FlowControl::Exception
|
||||
| FlowControl::IndirectBranch
|
||||
| FlowControl::IndirectCall
|
||||
| FlowControl::Interrupt
|
||||
| FlowControl::Next
|
||||
| FlowControl::Return
|
||||
| FlowControl::XbeginXabortXend => None,
|
||||
}
|
||||
}
|
176
src/agent/coverage/src/block/windows.rs
Normal file
176
src/agent/coverage/src/block/windows.rs
Normal file
@ -0,0 +1,176 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::process::Command;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use anyhow::Result;
|
||||
use debugger::{
|
||||
dbghelp::SymInfo,
|
||||
debugger::{BreakpointId, BreakpointType, DebugEventHandler, Debugger},
|
||||
target::Module,
|
||||
};
|
||||
|
||||
use crate::{AppCoverageBlocks, ModuleCoverageBlocks};
|
||||
|
||||
pub fn record(cmd: Command) -> Result<AppCoverageBlocks> {
|
||||
let mut handler = BlockCoverageHandler::new();
|
||||
|
||||
let (mut dbg, _child) = Debugger::init(cmd, &mut handler)?;
|
||||
dbg.run(&mut handler)?;
|
||||
|
||||
Ok(handler.coverage)
|
||||
}
|
||||
|
||||
pub struct BlockCoverageHandler {
|
||||
bp_to_block: BTreeMap<BreakpointId, (usize, usize)>,
|
||||
coverage: AppCoverageBlocks,
|
||||
started: Instant,
|
||||
max_duration: Duration,
|
||||
timed_out: bool,
|
||||
}
|
||||
|
||||
impl BlockCoverageHandler {
|
||||
pub fn new() -> Self {
|
||||
let coverage = AppCoverageBlocks::new();
|
||||
let bp_to_block = BTreeMap::default();
|
||||
let started = Instant::now();
|
||||
let max_duration = Duration::from_secs(5);
|
||||
let timed_out = false;
|
||||
|
||||
Self {
|
||||
bp_to_block,
|
||||
coverage,
|
||||
max_duration,
|
||||
started,
|
||||
timed_out,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pc(&self, dbg: &mut Debugger) -> Result<(u64, Option<SymInfo>)> {
|
||||
use iced_x86::Register::RIP;
|
||||
|
||||
let pc = dbg.read_register_u64(RIP)?;
|
||||
let sym = dbg.get_symbol(pc).ok();
|
||||
|
||||
Ok((pc, sym))
|
||||
}
|
||||
|
||||
fn add_module(&mut self, dbg: &mut Debugger, module: &Module) {
|
||||
let bitset = crate::pe::process_image(module.path(), false);
|
||||
let bitset = match bitset {
|
||||
Ok(bitset) => bitset,
|
||||
Err(err) => {
|
||||
// If we can't add the module, continue debugging.
|
||||
// We don't expect to have symbols for every module.
|
||||
log::warn!(
|
||||
"cannot record coverage for module = {}, err = {}",
|
||||
module.path().display(),
|
||||
err,
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let module_coverage = ModuleCoverageBlocks::new(module.path(), module.name(), bitset);
|
||||
|
||||
let m = self.coverage.add_module(module_coverage);
|
||||
let module_coverage = &self.coverage.modules()[m];
|
||||
for (b, block) in module_coverage.blocks().iter().enumerate() {
|
||||
let bp =
|
||||
dbg.register_breakpoint(module.name(), block.rva() as u64, BreakpointType::OneTime);
|
||||
self.bp_to_block.insert(bp, (m, b));
|
||||
}
|
||||
|
||||
log::debug!(
|
||||
"inserted {} breakpoints for module {}",
|
||||
module_coverage.blocks().len(),
|
||||
module.path().display(),
|
||||
);
|
||||
}
|
||||
|
||||
fn stop(&self, dbg: &mut Debugger) {
|
||||
dbg.quit_debugging();
|
||||
}
|
||||
|
||||
fn try_on_create_process(&mut self, dbg: &mut Debugger, module: &Module) -> Result<()> {
|
||||
dbg.target().sym_initialize()?;
|
||||
|
||||
log::info!(
|
||||
"exe loaded: {}, {} bytes",
|
||||
module.path().display(),
|
||||
module.image_size(),
|
||||
);
|
||||
|
||||
self.add_module(dbg, module);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn try_on_load_dll(&mut self, dbg: &mut Debugger, module: &Module) -> Result<()> {
|
||||
log::info!(
|
||||
"dll loaded: {}, {} bytes",
|
||||
module.path().display(),
|
||||
module.image_size(),
|
||||
);
|
||||
|
||||
self.add_module(dbg, module);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn try_on_breakpoint(&mut self, dbg: &mut Debugger, bp: BreakpointId) -> Result<()> {
|
||||
let (pc, _sym) = self.pc(dbg)?;
|
||||
|
||||
if let Some(&(m, b)) = self.bp_to_block.get(&bp) {
|
||||
if log::max_level() == log::Level::Trace {
|
||||
let module = &self.coverage.modules()[m];
|
||||
let block = &module.blocks()[b];
|
||||
let name = module.name().display();
|
||||
log::trace!("{:>16x}: {}+{:x}", pc, name, block.rva());
|
||||
}
|
||||
|
||||
self.coverage.report_block_hit(m, b);
|
||||
} else {
|
||||
log::error!("no block for breakpoint");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn try_on_poll(&mut self, dbg: &mut Debugger) -> Result<()> {
|
||||
if !self.timed_out && self.started.elapsed() > self.max_duration {
|
||||
self.timed_out = true;
|
||||
dbg.quit_debugging();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl DebugEventHandler for BlockCoverageHandler {
|
||||
fn on_create_process(&mut self, dbg: &mut Debugger, module: &Module) {
|
||||
if self.try_on_create_process(dbg, module).is_err() {
|
||||
self.stop(dbg);
|
||||
}
|
||||
}
|
||||
|
||||
fn on_load_dll(&mut self, dbg: &mut Debugger, module: &Module) {
|
||||
if self.try_on_load_dll(dbg, module).is_err() {
|
||||
self.stop(dbg);
|
||||
}
|
||||
}
|
||||
|
||||
fn on_breakpoint(&mut self, dbg: &mut Debugger, bp: BreakpointId) {
|
||||
if self.try_on_breakpoint(dbg, bp).is_err() {
|
||||
self.stop(dbg);
|
||||
}
|
||||
}
|
||||
|
||||
fn on_poll(&mut self, dbg: &mut Debugger) {
|
||||
if self.try_on_poll(dbg).is_err() {
|
||||
self.stop(dbg);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,13 +1,16 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#![cfg(windows)]
|
||||
#![allow(clippy::as_conversions)]
|
||||
#![allow(clippy::new_without_default)]
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
mod intel;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub mod pe;
|
||||
|
||||
pub mod block;
|
||||
|
||||
use std::{
|
||||
ffi::OsString,
|
||||
fs::File,
|
||||
@ -121,6 +124,10 @@ impl AppCoverageBlocks {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn count_blocks(&self) -> usize {
|
||||
self.modules().iter().map(|m| m.blocks().len()).sum()
|
||||
}
|
||||
|
||||
pub fn count_blocks_hit(&self) -> usize {
|
||||
self.modules().iter().map(|m| m.count_blocks_hit()).sum()
|
||||
}
|
||||
@ -128,6 +135,7 @@ impl AppCoverageBlocks {
|
||||
|
||||
/// Statically analyze the specified images to discover the basic block
|
||||
/// entry points and write out the results in a file in `output_dir`.
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn run_init(output_dir: PathBuf, modules: Vec<PathBuf>, function: bool) -> Result<()> {
|
||||
let mut result = AppCoverageBlocks::new();
|
||||
for module in modules {
|
||||
|
@ -45,7 +45,7 @@ use crate::{
|
||||
const STATUS_WX86_BREAKPOINT: u32 = ::winapi::shared::ntstatus::STATUS_WX86_BREAKPOINT as u32;
|
||||
|
||||
/// Uniquely identify a breakpoint.
|
||||
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
|
||||
#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub struct BreakpointId(pub u32);
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
|
Reference in New Issue
Block a user